Commit 48443d20 authored by Florian Unglaub's avatar Florian Unglaub

Merge branch 'master' of git://github.com/gitlabhq/gitlabhq

parents c5ae1549 6d4ae75f
#this code temporarily disables notes for all controllers
# Footnotes::Filter.notes = []
v 2.8.1
- ability to disable gravatars
- improved MR diff logic
- ssh key help page
v 2.8.0
- Gitlab Flavored Markdown
- Bulk issues update
- Issues API
- Cucumber coverage increased
- Post-receive files fixed
- UI improved
- Application cleanup
- more cucumber
- capybara-webkit + headless
v 2.7.0
- Issue Labels
......
......@@ -58,7 +58,7 @@ gem "unicorn"
gem "acts-as-taggable-on", "2.3.1"
# Decorators
gem "drapper"
gem "draper"
# Background jobs
gem "resque", "~> 1.20.0"
......@@ -80,10 +80,6 @@ gem 'settingslogic'
gem "foreman"
gem "git"
# Unused
gem 'tabs_on_rails'
gem "acts_as_list"
group :assets do
gem "sass-rails", "3.2.5"
gem "coffee-rails", "3.2.2"
......@@ -95,12 +91,11 @@ group :assets do
gem "jquery-ui-rails", "0.5.0"
gem "modernizr", "2.5.3"
gem "raphael-rails", "1.5.2"
gem 'bootstrap-sass', "2.0.3.1"
gem 'bootstrap-sass', "2.0.4"
end
group :development do
gem "letter_opener"
gem "rails-footnotes"
gem "annotate", :git => "https://github.com/ctran/annotate_models.git"
gem 'rack-mini-profiler'
end
......@@ -109,6 +104,7 @@ group :development, :test do
gem "rspec-rails"
gem "capybara"
gem "capybara-webkit"
gem "headless"
gem "autotest"
gem "autotest-rails"
gem "pry"
......@@ -119,11 +115,13 @@ end
group :test do
gem 'cucumber-rails', :require => false
gem 'minitest', ">= 2.10"
gem "turn", :require => false
gem "simplecov", :require => false
gem "shoulda-matchers"
gem 'email_spec'
gem 'resque_spec'
gem "webmock"
end
group :production do
gem "gitlab_meta", '2.8'
end
......@@ -98,9 +98,7 @@ GEM
multi_json (~> 1.0)
acts-as-taggable-on (2.3.1)
rails (~> 3.0)
acts_as_list (0.1.6)
addressable (2.2.8)
ansi (1.4.2)
arel (3.0.2)
autotest (4.4.6)
ZenTest (>= 4.4.1)
......@@ -109,7 +107,7 @@ GEM
awesome_print (1.0.2)
bcrypt-ruby (3.0.1)
blankslate (2.1.2.4)
bootstrap-sass (2.0.3.1)
bootstrap-sass (2.0.4.0)
builder (3.0.0)
capybara (1.1.2)
mime-types (>= 1.16)
......@@ -157,7 +155,9 @@ GEM
railties (~> 3.1)
warden (~> 1.2.1)
diff-lcs (1.1.3)
drapper (0.8.4)
draper (0.17.0)
actionpack (~> 3.2)
activesupport (~> 3.2)
email_spec (1.2.1)
mail (~> 2.2)
rspec (~> 2.0)
......@@ -175,6 +175,7 @@ GEM
gherkin (2.11.0)
json (>= 1.4.6)
git (1.2.5)
gitlab_meta (2.8)
grape (0.2.1)
hashie (~> 1.2)
multi_json
......@@ -189,6 +190,7 @@ GEM
railties (~> 3.0)
hashery (1.4.0)
hashie (1.2.0)
headless (0.3.1)
hike (1.2.1)
httparty (0.8.3)
multi_json (~> 1.0)
......@@ -223,7 +225,6 @@ GEM
treetop (~> 1.4.8)
method_source (0.7.1)
mime-types (1.19)
minitest (3.1.0)
modernizr (2.5.3)
sprockets (~> 2.0)
multi_json (1.3.6)
......@@ -286,8 +287,6 @@ GEM
activesupport (= 3.2.8)
bundler (~> 1.0)
railties (= 3.2.8)
rails-footnotes (3.7.8)
rails (>= 3.0.0)
railties (3.2.8)
actionpack (= 3.2.8)
activesupport (= 3.2.8)
......@@ -366,7 +365,6 @@ GEM
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.6)
stamp (0.1.6)
tabs_on_rails (2.1.1)
therubyracer (0.10.1)
libv8 (~> 3.3.10)
thin (1.3.1)
......@@ -378,8 +376,6 @@ GEM
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
turn (0.9.5)
ansi
tzinfo (0.3.33)
uglifier (1.0.3)
execjs (>= 0.3.0)
......@@ -403,12 +399,11 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on (= 2.3.1)
acts_as_list
annotate!
autotest
autotest-rails
awesome_print
bootstrap-sass (= 2.0.3.1)
bootstrap-sass (= 2.0.4)
capybara
capybara-webkit
carrierwave
......@@ -419,16 +414,18 @@ DEPENDENCIES
cucumber-rails
database_cleaner
devise (~> 2.1.0)
drapper
draper
email_spec
ffaker
foreman
git
gitlab_meta (= 2.8)
gitolite!
grack!
grape (~> 0.2.1)
grit!
haml-rails
headless
httparty
jquery-rails (= 2.0.2)
jquery-ui-rails (= 0.5.0)
......@@ -436,7 +433,6 @@ DEPENDENCIES
launchy
letter_opener
linguist (~> 1.0.0)!
minitest (>= 2.10)
modernizr (= 2.5.3)
mysql2
omniauth
......@@ -448,7 +444,6 @@ DEPENDENCIES
pygments.rb!
rack-mini-profiler
rails (= 3.2.8)
rails-footnotes
raphael-rails (= 1.5.2)
redcarpet (~> 2.1.1)
resque (~> 1.20.0)
......@@ -463,10 +458,8 @@ DEPENDENCIES
six
sqlite3
stamp
tabs_on_rails
therubyracer
thin
turn
uglifier (= 1.0.3)
unicorn
webmock
......
......@@ -7,8 +7,6 @@
//= require jquery
//= require jquery.ui.all
//= require jquery_ujs
//= require jquery.ui.selectmenu
//= require jquery.tagify
//= require jquery.cookie
//= require jquery.endless-scroll
//= require jquery.highlight
......@@ -74,7 +72,7 @@ $(document).ready(function(){
* Note markdown preview
*
*/
$('#preview-link').on('click', function(e) {
$(document).on('click', '#preview-link', function(e) {
$('#preview-note').text('Loading...');
var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview');
......
......@@ -112,6 +112,7 @@ var MergeRequest = {
already_cannot_be_merged:
function(){
$(".automerge_widget").hide();
$(".merge_in_progress").hide();
$(".automerge_widget.already_cannot_be_merged").show();
}
}
......@@ -3,8 +3,7 @@
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
* the top of the compiled file, but it's generally better to create a new file per style scope.
*= require jquery.ui.all
*= require jquery-ui/jquery.ui.selectmenu
*= require jquery-ui/jquery.tagify
*= require jquery.ui.aristo
*= require chosen
*= require_self
*= require main
......
......@@ -735,3 +735,11 @@ li.note {
font-size: 12px;
}
}
.error_message {
@extend .cred;
border-bottom: 1px solid #D21;
padding-bottom:20px;
text-align:center;
margin-bottom:10px;
}
body {
margin-bottom:20px;
}
pre {
font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
&.dark {
background: #333;
color:#f5f5f5;
}
}
a {
outline: none;
color: $link_color;
......@@ -325,16 +335,20 @@ img.avatar {
float:left;
margin-right:15px;
width:40px;
border:2px solid #ddd;
border:1px solid #ddd;
padding:1px;
&.s16 {
width:16px;
height:16px;
}
&.s24 {
width:24px;
height:24px;
}
&.s32 {
width:32px;
height:32px;
}
}
......
/**
* JQUERY UI datepicker
*
*/
.ui-datepicker {
border-color:#eee;
padding:20px;
.ui-state-default {
background:#f1f1f1;
padding:5px;
}
.ui-state-active {
background:#fff;
}
}
/**
* JQUERY UI progressbar
*
*/
.ui-progressbar {
border:1px solid #ddd;
height:6px;
margin:0;
padding:0;
.ui-progressbar-value {
background-color: #62C462;//$blue_link;
margin:0;
}
}
......@@ -23,6 +23,8 @@ $blue_link: #2fa0bb;
$style_color: #474D57;
$hover: #FDF5D9;
/** GITLAB Fonts **/
@font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); }
/** MIXINS **/
@mixin shade {
......@@ -165,9 +167,3 @@ $hover: #FDF5D9;
*
*/
@import "highlight/dark.scss";
/**
* JQUERY UI ext
*
*/
@import "jquery_ui.scss";
......@@ -26,23 +26,25 @@ header {
float:left;
position:relative;
top:-5px;
a {
float:left;
h1 {
text-indent:-9999px;
padding-top: 5px;
width:102px;
background: url('logo_text.png') no-repeat 0px -3px;
background: url('logo_dark.png') no-repeat 0px -3px;
float:left;
margin-left:5px;
font-size:20px;
font-size:36px;
line-height:36px;
font-weight:bold;
color:#aaa;
font-weight:normal;
color:$style_color;
text-shadow: 0 1px 1px #FFF;
padding-left:50px;
height:40px;
font-family: 'Korolev', sans-serif;
}
}
.separator {
margin-left:20px;
......@@ -68,14 +70,16 @@ header {
*
*/
.project_name {
position:relative;
float:left;
margin:0;
margin-right:30px;
font-size:24px;
font-size:36px;
line-height:36px;
font-weight:500;
font-weight:normal;
color:$style_color;
text-shadow: 0 1px 1px #FFF;
font-family: 'Korolev', sans-serif;
}
.fbtn {
......
......@@ -27,6 +27,7 @@ body.login-page{
-moz-border-radius-topright: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
margin-bottom:0px;
}
.login-box input.text.bottom{
......
......@@ -94,3 +94,8 @@ li.merge_request {
padding-bottom: 2px;
}
}
.merge_in_progress {
@extend .padded;
@extend .append-bottom-10;
}
......@@ -48,7 +48,7 @@
p { color:$style_color; }
.note-author { color: $style_color;}
.note-title { margin-left:50px; padding-top: 5px;}
.note-title { margin-left:45px; padding-top: 5px;}
.avatar {
margin-top:3px;
}
......
.projects {
.projects {
@extend .row;
.activities {
}
.side {
.side {
@extend .span4;
@extend .right;
.projects_box {
h5 {
.projects_box {
h5 {
color:$style_color;
font-size:16px;
text-shadow: 0 1px 1px #fff;
padding: 2px 10px;
}
@extend .leftbar;
@extend .ui-box;
......@@ -19,21 +20,22 @@
}
}
.new_project,
.edit_project {
.project_name_holder {
.new_project,
.edit_project {
.project_name_holder {
input,
label {
label {
font-size:16px;
line-height:20px;
padding:8px;
}
label {
label {
color:#888;
}
.btn {
.btn {
padding:6px;
margin-left:10px;
margin-bottom:8px;
}
}
}
......@@ -15,4 +15,36 @@
color: $blue_link;
}
}
header {
.fbtn {
.btn {
background-color: #F8F8F8;
background-image: -webkit-gradient(linear,left top,left bottom,from(#F8F8F8),to(#ECECEC));
background-image: -webkit-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: -moz-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: -ms-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: -o-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: linear-gradient(top,#F8F8F8,#ECECEC);
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#f8f8f8',EndColorStr='#ececec');
border-color: #C6C6C6;
margin-left:7px;
@include border-radius(3px);
box-shadow:none;
color:#666;
}
}
.search {
.search-input {
@include border-radius(3px);
border-color: #C6C6C6;
box-shadow:none;
}
}
.pic {
img {
@include border-radius(3px);
}
}
}
}
......@@ -20,6 +20,10 @@
.fbtn {
.btn {
i {
position: relative;
top: 1px;
}
margin-left:8px;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #595D63), to(#31363E));
background-image: -webkit-linear-gradient(#595D63 6.6%, #31363E);
......@@ -32,6 +36,10 @@
background-image: -moz-linear-gradient(#595D63 6.6%, #202227);
background-image: -o-linear-gradient(#595D63 6.6%, #202227);
background-position:0 0;
color:#fff;
i {
@extend .icon-white;
}
}
border: 1px solid #31363E;
......@@ -59,14 +67,9 @@
.app_logo {
a {
h1 {
background: url('images.png') no-repeat -3px -6px;
width: 65px;
height: 26px;
margin: 6px 0;
padding: 0;
float: left;
text-indent: -1000em;
float:left;
background: url('logo_white.png') no-repeat 0px -3px;
color:#fff;
text-shadow: 0 1px 1px #111;
}
}
.separator {
......@@ -75,7 +78,6 @@
}
.project_name {
line-height:38px;
color:#fff;
text-shadow: 0 1px 1px #111;
}
......
......@@ -37,26 +37,20 @@
*
*/
.app_logo {
width:160px;
a {
h1 {
opacity: 0.7;
background: url('images.png') no-repeat -3px -6px;
width: 65px;
height: 26px;
margin: 6px 0;
padding: 0;
float: left;
text-indent: -1000em;
float:left;
&:hover {
opacity: 1.0;
}
background: none;
color:#DDD;
font-size:30px;
text-shadow: 0 1px 1px #111;
padding-left: 0;
}
}
.separator {
width: 1px;
height: 40px;
margin: 0 9px;
margin: 0 10px;
overflow: hidden;
background: #222;
border-left: 1px solid #333;
......@@ -66,7 +60,6 @@
.fbtn {
.btn {
i {
@extend .icon-white;
position: relative;
top: 2px;
}
......@@ -77,10 +70,14 @@
color:#ccc;
&:hover {
color:#fff;
i {
@extend .icon-white;
}
}
border: none;
box-shadow:none;
text-shadow: 0 -1px 0 #000000;
border-left: 1px solid #333;
}
}
......@@ -113,9 +110,9 @@
*
*/
.project_name {
line-height:34px;
font-size:22px;
color:#fff;
line-height:36px;
font-size:30px;
color:#DDD;
text-shadow: 0 1px 1px #111;
}
......
class LabelsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any issue
before_filter :authorize_read_issue!
respond_to :js, :html
def index
@labels = Issue.tag_counts_on(:labels)
end
protected
def module_enabled
return render_404 unless @project.issues_enabled
end
end
......@@ -12,8 +12,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def ldap
# We only find ourselves here if the authentication to LDAP was successful.
info = request.env["omniauth.auth"]["info"]
@user = User.find_for_ldap_auth(info)
@user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user)
if @user.persisted?
@user.remember_me = true
end
......@@ -39,7 +38,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
current_user.save
redirect_to profile_path
else
@user = User.find_by_provider_and_uid(provider, uid)
@user = User.find_by_provider_and_extern_uid(provider, uid)
if @user
sign_in_and_redirect @user
......
require File.join(Rails.root, 'lib', 'graph_commit')
require Rails.root.join('lib', 'gitlab', 'graph_commit')
class ProjectsController < ApplicationController
before_filter :project, except: [:index, :new, :create]
......@@ -78,7 +78,7 @@ class ProjectsController < ApplicationController
end
def graph
@days_json, @commits_json = GraphCommit.to_graph(project)
@days_json, @commits_json = Gitlab::GraphCommit.to_graph(project)
end
def destroy
......
......@@ -9,6 +9,7 @@ class TeamMembersController < ApplicationController
def show
@team_member = project.users_projects.find(params[:id])
@events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7)
end
def new
......
class ApplicationDecorator < Drapper::Base
class ApplicationDecorator < Draper::Base
# Lazy Helpers
# PRO: Call Rails helpers without the h. proxy
# ex: number_to_currency(model.price)
......
......@@ -2,10 +2,13 @@ require 'digest/md5'
module ApplicationHelper
def gravatar_icon(user_email = '', size = 40)
return unless user_email
gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com"
user_email.strip!
"#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon"
if Gitlab.config.disable_gravatar? || user_email.blank?
'no_avatar.png'
else
gravatar_prefix = request.ssl? ? "https://secure" : "http://www"
user_email.strip!
"#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon"
end
end
def request_protocol
......
module GitlabMarkdownHelper
# Replaces references (i.e. @abc, #123, !456, ...) in the text with links to
# the appropriate items in Gitlab.
#
# text - the source text
# html_options - extra options for the reference links as given to link_to
#
# note: reference links will only be generated if @project is set
#
# see Gitlab::Markdown for details on the supported syntax
def gfm(text, html_options = {})
return text if text.nil?
return text if @project.nil?
# Extract pre blocks
# Extract pre blocks so they are not altered
# from http://github.github.com/github-flavored-markdown/
extractions = {}
text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
......@@ -25,7 +34,15 @@ module GitlabMarkdownHelper
text.html_safe
end
# circumvents nesting links, which will behave bad in browsers
# Use this in places where you would normally use link_to(gfm(...), ...).
#
# It solves a problem occurring with nested links (i.e.
# "<a>outer text <a>gfm ref</a> more outer text</a>"). This will not be
# interpreted as intended. Browsers will parse something like
# "<a>outer text </a><a>gfm ref</a> more outer text" (notice the last part is
# not linked any more). link_to_gfm corrects that. It wraps all parts to
# explicitly produce the correct linking behavior (i.e.
# "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
def link_to_gfm(body, url, html_options = {})
gfm_body = gfm(body, html_options)
......
......@@ -12,74 +12,102 @@ class Notify < ActionMailer::Base
def new_user_email(user_id, password)
@user = User.find(user_id)
@password = password
mail(to: @user.email, subject: "gitlab | Account was created for you")
mail(to: @user.email, subject: subject("Account was created for you"))
end
def new_issue_email(issue_id)
@issue = Issue.find(issue_id)
@project = @issue.project
mail(to: @issue.assignee_email, subject: "gitlab | new issue ##{@issue.id} | #{@issue.title} | #{@project.name}")
mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title))
end
def note_wall_email(recipient_id, note_id)
recipient = User.find(recipient_id)
@note = Note.find(note_id)
@project = @note.project
mail(to: recipient.email, subject: "gitlab | #{@project.name}")
mail(to: recipient(recipient_id), subject: subject)
end
def note_commit_email(recipient_id, note_id)
recipient = User.find(recipient_id)
@note = Note.find(note_id)
@commit = @note.target
@commit = CommitDecorator.decorate(@commit)
@project = @note.project
mail(to: recipient.email, subject: "gitlab | note for commit #{@commit.short_id} | #{@commit.title} | #{@project.name}")
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
end
def note_merge_request_email(recipient_id, note_id)
recipient = User.find(recipient_id)
@note = Note.find(note_id)
@merge_request = @note.noteable
@project = @note.project
mail(to: recipient.email, subject: "gitlab | note for merge request !#{@merge_request.id} | #{@project.name}")
mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}"))
end
def note_issue_email(recipient_id, note_id)
recipient = User.find(recipient_id)
@note = Note.find(note_id)
@issue = @note.noteable
@project = @note.project
mail(to: recipient.email, subject: "gitlab | note for issue ##{@issue.id} | #{@project.name}")
mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}"))
end
def note_wiki_email(recipient_id, note_id)
recipient = User.find(recipient_id)
@note = Note.find(note_id)
@wiki = @note.noteable
@project = @note.project
mail(to: recipient.email, subject: "gitlab | note for wiki | #{@project.name}")
mail(to: recipient(recipient_id), subject: subject("note for wiki"))
end
def new_merge_request_email(merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
mail(to: @merge_request.assignee_email, subject: "gitlab | new merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}")
mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
recipient = User.find(recipient_id)
@merge_request = MergeRequest.find(merge_request_id)
@previous_assignee ||= User.find(previous_assignee_id)
@project = @merge_request.project
mail(to: recipient.email, subject: "gitlab | changed merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}")
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
recipient = User.find(recipient_id)
@issue = Issue.find(issue_id)
@previous_assignee ||= User.find(previous_assignee_id)
@project = @issue.project
mail(to: recipient.email, subject: "gitlab | changed issue ##{@issue.id} | #{@issue.title} | #{@project.name}")
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
end
private
# Look up a User by their ID and return their email address
#
# recipient_id - User ID
#
# Returns a String containing the User's email address.
def recipient(recipient_id)
if recipient = User.find(recipient_id)
recipient.email
end
end
# Formats arguments into a String suitable for use as an email subject
#
# extra - Extra Strings to be inserted into the subject
#
# Examples
#
# >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum"
#
# # Automatically inserts Project name when @project is set
# >> @project = Project.last
# => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
# >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum | Ruby on Rails"
#
# # Accepts multiple arguments
# >> subject('Lorem ipsum', 'Dolor sit amet')
# => "gitlab | Lorem ipsum | Dolor sit amet"
def subject(*extra)
"gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "")
end
end
......@@ -9,8 +9,6 @@ class Issue < ActiveRecord::Base
validates :description,
length: { within: 0..2000 }
acts_as_list
def self.open_for(user)
opened.assigned(user)
end
......
......@@ -88,8 +88,11 @@ class MergeRequest < ActiveRecord::Base
end
def unmerged_diffs
commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
# Only show what is new in the source branch compared to the target branch, not the other way around.
# The linex below with merge_base is equivalent to diff with three dots (git diff branch1...branch2)
# From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B"
common_commit = project.repo.git.native(:merge_base, {}, [target_branch, source_branch]).strip
diffs = project.repo.diff(common_commit, source_branch)
end
def last_commit
......
......@@ -2,7 +2,7 @@ require "grit"
class Project < ActiveRecord::Base
include Repository
include ProjectPush
include PushObserver
include Authority
include Team
......
......@@ -7,7 +7,7 @@ class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password
:theme_id, :force_random_password, :extern_uid, :provider
attr_accessor :force_random_password
......@@ -54,6 +54,8 @@ class User < ActiveRecord::Base
validates :bio, length: { within: 0..255 }
validates :extern_uid, :allow_blank => true, :uniqueness => {:scope => :provider}
before_save :ensure_authentication_token
alias_attribute :private_token, :authentication_token
......@@ -84,21 +86,31 @@ class User < ActiveRecord::Base
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end
def self.find_for_ldap_auth(omniauth_info)
name = omniauth_info.name.force_encoding("utf-8")
email = omniauth_info.email.downcase unless omniauth_info.email.nil?
raise OmniAuth::Error, "LDAP accounts must provide an email address" if email.nil?
def self.find_for_ldap_auth(auth, signed_in_resource=nil)
uid = auth.info.uid
provider = auth.provider
name = auth.info.name.force_encoding("utf-8")
email = auth.info.email.downcase unless auth.info.email.nil?
raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil?
if @user = User.find_by_email(email)
if @user = User.find_by_extern_uid_and_provider(uid, provider)
@user
# workaround for backward compatibility
elsif @user = User.find_by_email(email)
logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}"
@user.update_attributes(:extern_uid => uid, :provider => provider)
@user
else
logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}"
password = Devise.friendly_token[0, 8].downcase
@user = User.create(
name: name,
email: email,
password: password,
password_confirmation: password,
projects_limit: Gitlab.config.default_projects_limit
:extern_uid => uid,
:provider => provider,
:name => name,
:email => email,
:password => password,
:password_confirmation => password,
:projects_limit => Gitlab.config.default_projects_limit
)
end
end
......
module ProjectPush
module PushObserver
def observe_push(oldrev, newrev, ref, user)
data = post_receive_data(oldrev, newrev, ref, user)
......
......@@ -30,26 +30,10 @@ module Repository
Commit.commits_between(repo, from, to)
end
def write_hooks
%w(post-receive).each do |hook|
write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook")))
end
end
def satellite
@satellite ||= Gitlab::Satellite.new(self)
end
def write_hook(name, content)
hook_file = File.join(path_to_repo, 'hooks', name)
File.open(hook_file, 'w') do |f|
f.write(content)
end
File.chmod(0775, hook_file)
end
def has_post_receive_file?
hook_file = File.join(path_to_repo, 'hooks', 'post-receive')
File.exists?(hook_file)
......@@ -73,8 +57,6 @@ module Repository
def update_repository
Gitlab::GitHost.system.update_project(path, self)
write_hooks if File.exists?(path_to_repo)
end
def destroy_repository
......
......@@ -5,7 +5,7 @@
Read more about system hooks
%strong #{link_to "here", help_system_hooks_path, class: "vlink"}
= form_for @hook, as: :hook, url: admin_hooks_path do |f|
= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f|
-if @hook.errors.any?
.alert-message.block-message.error
- @hook.errors.full_messages.each do |msg|
......
......@@ -2,7 +2,7 @@
Projects
= link_to 'New Project', new_admin_project_path, class: "btn small right"
%br
= form_tag admin_projects_path, method: :get do
= form_tag admin_projects_path, method: :get, class: 'form-inline' do
= text_field_tag :name, params[:name], class: "xlarge"
= submit_tag "Search", class: "btn submit primary"
......
......@@ -3,7 +3,7 @@
= link_to 'New User', new_admin_user_path, class: "btn small right"
%br
= form_tag admin_users_path, method: :get do
= form_tag admin_users_path, method: :get, class: 'form-inline' do
= text_field_tag :name, params[:name], class: "xlarge"
= submit_tag "Search", class: "btn submit primary"
%ul.nav.nav-pills
......
......@@ -5,10 +5,10 @@
%span.btn.disabled.grouped
%i.icon-comment
= @notes_count
= link_to patch_project_commit_path(@project, @commit.id), class: "btn small grouped" do
= link_to patch_project_commit_path(@project, @commit.id), class: "btn small grouped" do
%i.icon-download-alt
Get Patch
= link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do
Get Patch
= link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do
%strong Browse Code »
%h3.commit-title.page_title
= gfm @commit.title
......
- if @projects.any?
.projects
.activities.span8
- if current_user.require_ssh_key?
.alert.alert-error.padded
%span
You wont be able to pull/push project code unless you
%strong
= link_to new_key_path, class: "vlink" do
add new key
to your profile
= render 'shared/no_ssh'
- if @events.any?
.content_list= render @events
- else
......@@ -57,5 +50,5 @@
If you will be added to project - it will be displayed here
:javascript
:javascript
$(function(){ Pager.init(20); });
......@@ -23,5 +23,3 @@
= preserve do
sudo chmod -R 770 /home/git/repositories/
sudo chown -R git:git /home/git/repositories/
sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive
......@@ -31,3 +31,6 @@
%li
%h5= link_to "Gitlab Markdown", help_markdown_path
%li
%h5= link_to "SSH keys", help_ssh_path
%h3 Permissions
%h3.page_title Permissions
.back_link
= link_to help_path do
= link_to help_path do
&larr; to index
%hr
......
%h3.page_title SSH Keys
.back_link
= link_to help_path do
&larr; to index
%hr
%p.slead
SSH key allows you to establish a secure connection between your computer and Gitlab
%p.slead
To generate a new SSH key just open your terminal and use code below.
%pre.dark
ssh-keygen -t rsa -C "#{current_user.email}"
\# Creates a new ssh key using the provided email
\# Generating public/private rsa key pair...
%p.slead
Next just use code below to dump your public key and add to GITLAB SSH Keys
%pre.dark
cat ~/.ssh/id_rsa.pub
\# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc....
%h3 System hooks
.back_link
= link_to :back do
= link_to :back do
&larr; back
%hr
%p.slead
%p.slead
Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
%br
System Hooks can be used for logging or change information in LDAP server.
......
%h3 Web hooks
%h3.page_title Web hooks
.back_link
= link_to help_path do
= link_to help_path do
&larr; to index
%hr
%p.slead
Every Gitlab project can trigger a web server whenever the repo is pushed to.
%p.slead
Every Gitlab project can trigger a web server whenever the repo is pushed to.
%br
Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
%br
......
- bash_lexer = Pygments::Lexer[:bash]
%h3 Workflow
%h3.page_title Workflow
.back_link
= link_to help_path do
= link_to help_path do
&larr; to index
%hr
......@@ -9,25 +8,25 @@
%li
%p Clone project
.bash
%pre
%pre.dark
git clone git@example.com:project-name.git
%li
%p Create branch with your feature
.bash
%pre
%pre.dark
git checkout -b $feature_name
%li
%p Write code. Commit changes
.bash
%pre
%pre.dark
git commit -am "My feature is ready"
%li
%p Push your branch to gitlabhq
.bash
%pre
%pre.dark
git push origin $feature_name
%li
......
......@@ -8,7 +8,7 @@
Read more about web hooks
%strong #{link_to "here", help_web_hooks_path, class: "vlink"}
= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project) do |f|
= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-inline' } do |f|
-if @hook.errors.any?
.alert-message.block-message.error
- @hook.errors.full_messages.each do |msg|
......
%div.issue-form-holder
%h3= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}"
%h3.page_title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}"
= form_for [@project, @issue], remote: request.xhr? do |f|
-if @issue.errors.any?
.alert-message.block-message.error
......@@ -9,26 +9,26 @@
.issue_form_box
.issue_title
.clearfix
= f.label :title do
= f.label :title do
%strong= "Subject *"
.input
= f.text_field :title, maxlength: 255, class: "xxlarge"
.issue_middle_block
.issue_assignee
= f.label :assignee_id do
= f.label :assignee_id do
%i.icon-user
Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" })
.issue_milestone
= f.label :milestone_id do
= f.label :milestone_id do
%i.icon-time
Milestone
.input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" })
.issue_description
.clearfix
= f.label :label_list do
%i.icon-tag
= f.label :label_list do
%i.icon-tag
Labels
.input
= f.text_field :label_list, maxlength: 2000, class: "xxlarge"
......
......@@ -5,6 +5,9 @@
%li{class: "#{'active' if current_page?(project_milestones_path(@project))}"}
= link_to project_milestones_path(@project), class: "tab" do
Milestones
%li{class: "#{'active' if current_page?(project_labels_path(@project))}"}
= link_to project_labels_path(@project), class: "tab" do
Labels
%li.right
%span.rss-icon
= link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
......
......@@ -7,6 +7,7 @@
.span5
- if can? current_user, :write_issue, @project
= link_to new_project_issue_path(@project), class: "right btn small", title: "New Issue", remote: true do
%i.icon-plus
New Issue
= form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do
= hidden_field_tag :project_id, @project.id, { id: 'project_id' }
......@@ -21,7 +22,7 @@
.issues_bulk_update.hide
= form_tag bulk_update_project_issues_path(@project), method: :post do
= form_tag bulk_update_project_issues_path(@project), method: :post do
%span.update_issues_text Update selected issues with &nbsp;
.left
= select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status")
......@@ -53,7 +54,7 @@
= select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone")
= hidden_field_tag :f, params[:f]
.clearfix
%ul#issues-table.unstyled.issues_table
= render "issues"
......
......@@ -11,7 +11,13 @@
.input= f.text_field :title
.clearfix
= f.label :key
.input= f.text_area :key, class: [:xxlarge, :thin_area]
.input
= f.text_area :key, class: [:xxlarge, :thin_area]
%p.hint
Paste your public key here. Read more about how generate it
= link_to "here", help_ssh_path
.actions
= f.submit 'Save', class: "primary btn"
= link_to "Cancel", keys_path, class: "btn"
......
%h3.page_title New key
%h3.page_title Add an SSH Key
%hr
= render 'form'
......
%li.wll
%strong= label.name
.right
%span= pluralize label.count, 'issue'
= render "issues/head"
%h3.page_title
Labels
%br
%div.ui-box
%ul.unstyled.labels-table
- @labels.each do |label|
= render 'label', label: label
- unless @labels.present?
%li
%h3.nothing_here_message Nothing to show here
......@@ -6,7 +6,6 @@
= favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application"
= javascript_include_tag "application"
-# Atom feed
- if controller_name == 'projects' && action_name == 'index'
= auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed"
......
......@@ -33,7 +33,8 @@
});
$(".edit_merge_request").live("ajax:beforeSend", function() {
$(this).replaceWith('#{image_tag "ajax_loader.gif"}');
$('.can_be_merged').hide();
$('.merge_in_progress').show();
})
})
......@@ -3,13 +3,12 @@
%a.close{href: "#"} ×
%h3 How To Merge
.modal-body
%pre
%pre.dark
= preserve do
:erb
git checkout <%= @merge_request.target_branch %>
git fetch origin
git merge origin/<%= @merge_request.source_branch %>
git push origin <%= @merge_request.target_branch %>
git checkout #{@merge_request.target_branch}
git fetch origin
git merge origin/#{@merge_request.source_branch}
git push origin #{@merge_request.target_branch}
:javascript
......
......@@ -40,3 +40,6 @@
.alert.alert-info
%strong This merge request already can not be merged. Try to reload page.
.merge_in_progress.hide
%span.cgray Merge is in progress. Please wait. Page will be automatically reloaded. &nbsp;
= image_tag "ajax_loader.gif"
%h3= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}"
%h3.page_title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}"
.back_link
= link_to project_milestones_path(@project) do
&larr; To milestones
......@@ -17,12 +17,12 @@
= f.label :title, "Title", class: "control-label"
.controls
= f.text_field :title, maxlength: 255, class: "input-xlarge"
%p.help-block Required
%p.hint Required
.control-group
= f.label :description, "Description", class: "control-label"
.controls
= f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10
%p.help-block Markdown is enabled.
%p.hint Markdown is enabled.
.span6
.control-group
= f.label :due_date, "Due Date", class: "control-label"
......
- if note.valid?
:plain
$("#new_note .errors").remove();
$("#new_note .error").remove();
$('#new_note textarea').val("");
$('#preview-link').text('Preview');
$('#preview-note').hide(); $('#note_note').show();
NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}");
- else
:plain
......
%li{id: dom_id(note), class: "note"}
= image_tag gravatar_icon(note.author.email), class: "avatar"
= image_tag gravatar_icon(note.author.email), class: "avatar s32"
%div.note-author
%strong= note.author_name
= link_to "##{dom_id(note)}", name: dom_id(note) do
......
- if current_user.require_ssh_key?
.alert-message.block-message.error
%ul
%li You have no ssh keys added to your profile.
%li You wont be able to pull/push repository.
%li Visit profile &rarr; keys and add public key of every machine you want to use for work with gitlabhq.
.alert-message.block-message.error
%ul.unstyled.alert_holder
%li You should push repository to proceed.
%li After push you will be able to browse code, commits etc.
- bash_lexer = Pygments::Lexer[:bash]
= render 'shared/no_ssh'
%div.git-empty
%h3 Git global setup:
- setup_str = ["git config --global user.name \"#{current_user.name}\"",
"git config --global user.email \"#{current_user.email}\""].join("\n")
= preserve do
= raw bash_lexer.highlight(setup_str, lexer: 'bash', options: {encoding: 'utf-8'})
%br
%br
%h3 Create Repository
- repo_setup_str = ["mkdir #{@project.path}",
"cd #{@project.path}",
"git init",
"touch README",
"git add README",
"git commit -m 'first commit'",
"git remote add origin #{@project.url_to_repo}",
"git push -u origin master"].join("\n")
%h4 Git global setup:
%pre.dark
= preserve do
git config --global user.name "#{current_user.name}"
git config --global user.email "#{current_user.email}"
= preserve do
= raw bash_lexer.highlight(repo_setup_str)
%h4.prepend-top-20 Create Repository
%pre.dark
= preserve do
mkdir #{@project.path}
cd #{@project.path}
git init
touch README
git add README
git commit -m 'first commit'
git remote add origin #{@project.url_to_repo}
git push -u origin master
%br
%br
%h3 Existing Git Repo?
- exist_repo_setup_str = ["cd existing_git_repo",
"git remote add origin #{@project.url_to_repo}",
"git push -u origin master"].join("\n")
= preserve do
= raw bash_lexer.highlight(exist_repo_setup_str)
%h4.prepend-top-20 Existing Git Repo?
%pre.dark
= preserve do
cd existing_git_repo
git remote add origin #{@project.url_to_repo}
git push -u origin master
- if can? current_user, :admin_project, @project
.alert-message.block-message.error.prepend-top-20
= link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger"
.prepend-top-20
= link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right"
......@@ -51,8 +51,6 @@
:javascript
$(function(){
$('select#branch').selectmenu({style:'popup', width:200});
$('select#tag').selectmenu({style:'popup', width:200});
$('.project-refs-select').chosen();
history.pushState({ path: this.path }, '', "#{@history_path}");
......
- if tm
%strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
= link_to_gfm truncate(content_commit.title, length: tm ? 30 : 50), project_commit_path(@project, content_commit.id), class: "tree-commit-link"
= form_tag search_path, method: :get do |f|
= form_tag search_path, method: :get, class: 'form-inline' do |f|
.padded
= label_tag :search do
%strong Looking for
......
- if current_user.require_ssh_key?
%h6.error_message
%span
You wont be able to pull/push project code unless you
%strong
= link_to new_key_path, class: "vlink" do
add SSH key
to your profile
......@@ -9,7 +9,7 @@
%span.label Blocked
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
= image_tag gravatar_icon(user.email, 40), class: "avatar"
= image_tag gravatar_icon(user.email, 40), class: "avatar s32"
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
%strong= truncate(user.name, lenght: 40)
%br
......
......@@ -51,7 +51,7 @@
= form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f|
= f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin
%hr
= render user.recent_events.limit(5)
= render @events
:javascript
$(function(){
$('.repo-access-select, .project-access-select').live("change", function() {
......
......@@ -44,5 +44,8 @@ module Gitlab
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
# Add fonts
config.assets.paths << "#{Rails.root}/app/assets/fonts"
end
end
......@@ -23,7 +23,7 @@ app:
default_projects_limit: 10
# backup_path: "/vol/backups" # default: Rails.root + backups/
# backup_keep_time: 604800 # default: 0 (forever) (in seconds)
# disable_gravatar: true # default: false - Disable user avatars from Gravatar.com
#
# 2. Advanced settings:
......@@ -39,7 +39,6 @@ git_host:
receive_pack: true
# port: 22
# Git settings
# Use default values unless you understand it
git:
......
......@@ -120,5 +120,8 @@ class Settings < Settingslogic
omniauth['providers'] || []
end
def disable_gravatar?
app['disable_gravatar'] || false
end
end
end
#if defined?(Footnotes) && Rails.env.development?
#Footnotes.run! # first of all
#end
......@@ -30,6 +30,7 @@ Gitlab::Application.routes.draw do
get 'help/web_hooks' => 'help#web_hooks'
get 'help/system_hooks' => 'help#system_hooks'
get 'help/markdown' => 'help#markdown'
get 'help/ssh' => 'help#ssh'
#
# Admin Area
......@@ -196,7 +197,9 @@ Gitlab::Application.routes.draw do
end
resources :team_members
resources :milestones
resources :labels, :only => [:index]
resources :issues do
collection do
post :sort
post :bulk_update
......
class AddExternAuthProviderToUsers < ActiveRecord::Migration
def change
add_column :users, :extern_uid, :string
add_column :users, :provider, :string
add_index :users, [:extern_uid, :provider], :unique => true
end
end
class AddProviderAndUidToUsers < ActiveRecord::Migration
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
end
end
......@@ -171,11 +171,12 @@ ActiveRecord::Schema.define(:version => 20120803152018) do
t.boolean "blocked", :default => false, :null => false
t.integer "failed_attempts", :default => 0
t.datetime "locked_at"
t.string "extern_uid"
t.string "provider"
t.string "uid"
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
create_table "users_projects", :force => true do |t|
......
......@@ -119,7 +119,6 @@ Permissions:
sudo chmod -R g+rwX /home/git/repositories/
sudo chown -R git:git /home/git/repositories/
sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive
#### CHECK: Logout & login again to apply git group to your user
......@@ -134,7 +133,7 @@ Permissions:
# 4. Install gitlab and configuration. Check status configuration.
sudo gem install charlock_holmes
sudo gem install charlock_holmes --version '0.6.8'
sudo pip install pygments
sudo gem install bundler
cd /home/gitlab
......@@ -177,6 +176,11 @@ Permissions:
#### Setup DB
sudo -u gitlab bundle exec rake gitlab:app:setup RAILS_ENV=production
#### Setup gitlab hooks
sudo cp ./lib/hooks/post-receive /home/git/share/gitolite/hooks/common/post-receive
sudo chown git:git /home/git/share/gitolite/hooks/common/post-receive
Checking status:
......@@ -196,6 +200,7 @@ Checking status:
Resolving deltas: 100% (174/174), done.
Can clone gitolite-admin?............YES
UMASK for .gitolite.rc is 0007? ............YES
/home/git/share/gitolite/hooks/common/post-receive exists? ............YES
If you got all YES - congrats! You can go to next step.
......
Feature: Labels
Background:
Given I signin as a user
And I own project "Shop"
And project "Shop" have issues tags:
| name |
| bug |
| feature |
Given I visit project "Shop" labels page
Scenario: I should see active milestones
Then I should see label "bug"
And I should see label "feature"
......@@ -4,9 +4,7 @@ Feature: Project Network Graph
Background:
Given I signin as a user
And I own project "Shop"
And I visit project "Shop" network page
And I visit project "Shop" network page
Scenario: I should see project network
Then page should have network graph
......@@ -91,36 +91,28 @@ Then /^I should see my merge requests$/ do
end
Given /^I have assigned issues$/ do
project1 = Factory :project,
:path => "project1",
:code => "TEST1"
project2 = Factory :project,
:path => "project2",
:code => "TEST2"
project1.add_access(@user, :read, :write)
project2.add_access(@user, :read, :write)
project = Factory :project
project.add_access(@user, :read, :write)
issue1 = Factory :issue,
:author => @user,
:assignee => @user,
:project => project1
:project => project
issue2 = Factory :issue,
:author => @user,
:assignee => @user,
:project => project2
:project => project
end
Given /^I have authored merge requests$/ do
project1 = Factory :project,
:path => "project1",
:code => "TEST1"
:path => "gitlabhq_1",
:code => "gitlabhq_1"
project2 = Factory :project,
:path => "project2",
:code => "TEST2"
:path => "gitlabhq_2",
:code => "gitlabhq_2"
project1.add_access(@user, :read, :write)
project2.add_access(@user, :read, :write)
......
......@@ -33,6 +33,25 @@ Given /^I visit issue page "(.*?)"$/ do |arg1|
end
Given /^I submit new issue "(.*?)"$/ do |arg1|
fill_in "issue_title", :with => arg1
fill_in "issue_title", with: arg1
click_button "Submit new issue"
end
Given /^project "(.*?)" have issues tags:$/ do |arg1, table|
project = Project.find_by_name(arg1)
table.hashes.each do |hash|
Factory :issue,
project: project,
label_list: [hash[:name]]
end
end
Given /^I visit project "(.*?)" labels page$/ do |arg1|
visit project_labels_path(Project.find_by_name(arg1))
end
Then /^I should see label "(.*?)"$/ do |arg1|
within ".labels-table" do
page.should have_content arg1
end
end
......@@ -57,6 +57,11 @@ end
Given /^I visit project "(.*?)" network page$/ do |arg1|
project = Project.find_by_name(arg1)
# Stub out find_all to speed this up (10 commits vs. 650)
commits = Grit::Commit.find_all(project.repo, nil, {max_count: 10})
Grit::Commit.stub(:find_all).and_return(commits)
visit graph_project_path(project)
end
......@@ -67,8 +72,8 @@ end
Given /^page should have network graph$/ do
page.should have_content "Project Network Graph"
within ".graph" do
page.should have_content "stable"
page.should have_content "notes_refacto..."
page.should have_content "master"
page.should have_content "scss_refactor..."
end
end
......
require 'simplecov'
SimpleCov.start 'rails'
unless ENV['CI']
require 'simplecov'
SimpleCov.start 'rails'
end
require 'cucumber/rails'
require 'webmock/cucumber'
......@@ -39,3 +41,10 @@ rescue NameError
end
Cucumber::Rails::Database.javascript_strategy = :truncation
require 'headless'
headless = Headless.new
headless.start
require 'cucumber/rspec/doubles'
module Color
extend self
def colorize(text, color_code)
"\033[#{color_code}#{text}\033[0m"
end
def red(text)
colorize(text, "31m")
end
def green(text)
colorize(text, "32m")
end
def yellow(text)
colorize(text, "93m")
end
def command(string)
`#{string}`
if $?.to_i > 0
puts red " == #{string} - FAIL"
puts red " == Error during configure"
exit
else
puts green " == #{string} - OK"
end
end
end
require "grit"
module Gitlab
class GraphCommit
attr_accessor :time, :space
attr_accessor :refs
def self.to_graph(project)
@repo = project.repo
commits = Grit::Commit.find_all(@repo, nil, {max_count: 650})
ref_cache = {}
commits.map! {|c| GraphCommit.new(Commit.new(c))}
commits.each { |commit| commit.add_refs(ref_cache, @repo) }
days = GraphCommit.index_commits(commits)
@days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json
@commits_json = commits.map(&:to_graph_hash).to_json
return @days_json, @commits_json
end
# Method is adding time and space on the
# list of commits. As well as returns date list
# corelated with time set on commits.
#
# @param [Array<GraphCommit>] comits to index
#
# @return [Array<TimeDate>] list of commit dates corelated with time on commits
def self.index_commits(commits)
days, heads = [], []
map = {}
commits.reverse.each_with_index do |c,i|
c.time = i
days[i] = c.committed_date
map[c.id] = c
heads += c.refs unless c.refs.nil?
end
heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}
# sort heads so the master is top and current branches are closer
heads.sort! do |a,b|
if a.name == "master"
-1
elsif b.name == "master"
1
else
b.commit.committed_date <=> a.commit.committed_date
end
end
@_reserved = {}
days.each_index do |i|
@_reserved[i] = []
end
heads.each do |h|
if map.include? h.commit.id then
place_chain(map[h.commit.id], map)
end
end
days
end
# Add space mark on commit and its parents
#
# @param [GraphCommit] the commit object.
# @param [Hash<String,GraphCommit>] map of commits
def self.place_chain(commit, map, parent_time = nil)
leaves = take_left_leaves(commit, map)
if leaves.empty? then
return
end
space = find_free_space(leaves.last.time..leaves.first.time)
leaves.each{|l| l.space = space}
# and mark it as reserved
min_time = leaves.last.time
parents = leaves.last.parents.collect
parents.each do |p|
if map.include? p.id then
parent = map[p.id]
if parent.time < min_time then
min_time = parent.time
end
end
end
if parent_time.nil? then
max_time = leaves.first.time
else
max_time = parent_time - 1
end
mark_reserved(min_time..max_time, space)
# Visit branching chains
leaves.each do |l|
parents = l.parents.collect
.select{|p| map.include? p.id and map[p.id].space == 0}
for p in parents
place_chain(map[p.id], map, l.time)
end
end
end
def self.mark_reserved(time_range, space)
for day in time_range
@_reserved[day].push(space)
end
end
def self.find_free_space(time_range)
reserved = []
for day in time_range
reserved += @_reserved[day]
end
space = 1
while reserved.include? space do
space += 1
end
space
end
# Takes most left subtree branch of commits
# which don't have space mark yet.
#
# @param [GraphCommit] the commit object.
# @param [Hash<String,GraphCommit>] map of commits
#
# @return [Array<GraphCommit>] list of branch commits
def self.take_left_leaves(commit, map)
leaves = []
leaves.push(commit) if commit.space == 0
while true
parent = commit.parents.collect
.select{|p| map.include? p.id and map[p.id].space == 0}
if parent.count == 0 then
return leaves
else
commit = map[parent.first.id]
leaves.push(commit)
end
end
end
def initialize(commit)
@_commit = commit
@time = -1
@space = 0
end
def method_missing(m, *args, &block)
@_commit.send(m, *args, &block)
end
def to_graph_hash
h = {}
h[:parents] = self.parents.collect do |p|
[p.id,0,0]
end
h[:author] = Gitlab::Encode.utf8(author.name)
h[:time] = time
h[:space] = space
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha
h[:date] = date
h[:message] = Gitlab::Encode.utf8(message)
h[:login] = author.email
h
end
def add_refs(ref_cache, repo)
if ref_cache.empty?
repo.refs.each do |ref|
ref_cache[ref.commit.id] ||= []
ref_cache[ref.commit.id] << ref
end
end
@refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)
@refs ||= []
end
end
end
module Gitlab
# Custom parsing for Gitlab-flavored Markdown
# Custom parser for Gitlab-flavored Markdown
#
# It replaces references in the text with links to the appropriate items in Gitlab.
#
# Supported reference formats are:
# * @foo for team members
# * #123 for issues
# * !123 for merge requests
# * $123 for snippets
# * 123456 for commits
#
# Examples
#
......@@ -67,25 +76,25 @@ module Gitlab
def reference_user(identifier)
if user = @project.users.where(name: identifier).first
member = @project.users_projects.where(user_id: user).first
link_to("@#{user.name}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member
link_to("@#{identifier}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member
end
end
def reference_issue(identifier)
if issue = @project.issues.where(id: identifier).first
link_to("##{issue.id}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}"))
link_to("##{identifier}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}"))
end
end
def reference_merge_request(identifier)
if merge_request = @project.merge_requests.where(id: identifier).first
link_to("!#{merge_request.id}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}"))
link_to("!#{identifier}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}"))
end
end
def reference_snippet(identifier)
if snippet = @project.snippets.where(id: identifier).first
link_to("$#{snippet.id}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}"))
link_to("$#{identifier}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}"))
end
end
......
require "grit"
class GraphCommit
attr_accessor :time, :space
attr_accessor :refs
def self.to_graph(project)
@repo = project.repo
commits = Grit::Commit.find_all(@repo, nil, {max_count: 650})
ref_cache = {}
commits.map! {|c| GraphCommit.new(Commit.new(c))}
commits.each { |commit| commit.add_refs(ref_cache, @repo) }
days = GraphCommit.index_commits(commits)
@days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json
@commits_json = commits.map(&:to_graph_hash).to_json
return @days_json, @commits_json
end
# Method is adding time and space on the
# list of commits. As well as returns date list
# corelated with time set on commits.
#
# @param [Array<GraphCommit>] comits to index
#
# @return [Array<TimeDate>] list of commit dates corelated with time on commits
def self.index_commits(commits)
days, heads = [], []
map = {}
commits.reverse.each_with_index do |c,i|
c.time = i
days[i] = c.committed_date
map[c.id] = c
heads += c.refs unless c.refs.nil?
end
heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote}
# sort heads so the master is top and current branches are closer
heads.sort! do |a,b|
if a.name == "master"
-1
elsif b.name == "master"
1
else
b.commit.committed_date <=> a.commit.committed_date
end
end
@_reserved = {}
days.each_index do |i|
@_reserved[i] = []
end
heads.each do |h|
if map.include? h.commit.id then
place_chain(map[h.commit.id], map)
end
end
days
end
# Add space mark on commit and its parents
#
# @param [GraphCommit] the commit object.
# @param [Hash<String,GraphCommit>] map of commits
def self.place_chain(commit, map, parent_time = nil)
leaves = take_left_leaves(commit, map)
if leaves.empty? then
return
end
space = find_free_space(leaves.last.time..leaves.first.time)
leaves.each{|l| l.space = space}
# and mark it as reserved
min_time = leaves.last.time
parents = leaves.last.parents.collect
parents.each do |p|
if map.include? p.id then
parent = map[p.id]
if parent.time < min_time then
min_time = parent.time
end
end
end
if parent_time.nil? then
max_time = leaves.first.time
else
max_time = parent_time - 1
end
mark_reserved(min_time..max_time, space)
# Visit branching chains
leaves.each do |l|
parents = l.parents.collect
.select{|p| map.include? p.id and map[p.id].space == 0}
for p in parents
place_chain(map[p.id], map, l.time)
end
end
end
def self.mark_reserved(time_range, space)
for day in time_range
@_reserved[day].push(space)
end
end
def self.find_free_space(time_range)
reserved = []
for day in time_range
reserved += @_reserved[day]
end
space = 1
while reserved.include? space do
space += 1
end
space
end
# Takes most left subtree branch of commits
# which don't have space mark yet.
#
# @param [GraphCommit] the commit object.
# @param [Hash<String,GraphCommit>] map of commits
#
# @return [Array<GraphCommit>] list of branch commits
def self.take_left_leaves(commit, map)
leaves = []
leaves.push(commit) if commit.space == 0
while true
parent = commit.parents.collect
.select{|p| map.include? p.id and map[p.id].space == 0}
if parent.count == 0 then
return leaves
else
commit = map[parent.first.id]
leaves.push(commit)
end
end
end
def initialize(commit)
@_commit = commit
@time = -1
@space = 0
end
def method_missing(m, *args, &block)
@_commit.send(m, *args, &block)
end
def to_graph_hash
h = {}
h[:parents] = self.parents.collect do |p|
[p.id,0,0]
end
h[:author] = Gitlab::Encode.utf8(author.name)
h[:time] = time
h[:space] = space
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha
h[:date] = date
h[:message] = Gitlab::Encode.utf8(message)
h[:login] = author.email
h
end
def add_refs(ref_cache, repo)
if ref_cache.empty?
repo.refs.each do |ref|
ref_cache[ref.commit.id] ||= []
ref_cache[ref.commit.id] << ref
end
end
@refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)
@refs ||= []
end
end
namespace :dev do
desc "Prepare for development (run dev_user.sh first)"
task :repos => :environment do
key = `sudo -u gitlabdev -H cat /home/gitlabdev/.ssh/id_rsa.pub`
raise "\n *** Run ./lib/tasks/dev/user.sh first *** \n" if key.empty?
Key.create(:user_id => User.first, :key => key, :title => "gitlabdev")
puts "\n *** Clone diaspora from github"
`sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/diaspora/diaspora.git /home/gitlabdev/diaspora"`
puts "\n *** Push diaspora source to gitlab"
`sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/diaspora; git remote add local git@localhost:diaspora.git; git push local master; git push local --tags; git checkout -b api origin/api; git push local api; git checkout -b heroku origin/heroku; git push local heroku"`
puts "\n *** Clone rails from github"
`sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/rails/rails.git /home/gitlabdev/rails"`
puts "\n *** Push rails source to gitlab"
`sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/rails; git remote add local git@localhost:ruby_on_rails.git; git push local master; git push local --tags"`
puts "\n *** Clone rubinius from github"
`sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/rubinius/rubinius.git /home/gitlabdev/rubinius"`
puts "\n *** Push rubinius source to gitlab"
`sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/rubinius; git remote add local git@localhost:rubinius.git; git push local master; git push local --tags"`
end
end
namespace :dev do
desc "DEV | Run cucumber and rspec"
task :tests do
["cucumber", "rspec spec"].each do |cmd|
puts "Starting to run #{cmd}..."
system("bundle exec #{cmd}")
raise "#{cmd} failed!" unless $?.exitstatus == 0
end
end
end
sudo adduser \
--gecos 'gitlab dev user' \
--disabled-password \
--home /home/gitlabdev \
gitlabdev
sudo -i -u gitlabdev -H sh -c "ssh-keygen -t rsa"
namespace :gitlab do
namespace :app do
desc "GITLAB | Setup production application"
task :setup => ['db:setup', 'db:seed_fu', 'gitlab:app:enable_automerge']
task :setup => [
'db:setup',
'db:seed_fu',
'gitlab:app:enable_automerge'
]
end
end
......@@ -56,6 +56,20 @@ namespace :gitlab do
return
end
gitolite_hooks_path = File.join("/home", Gitlab.config.ssh_user, "share", "gitolite", "hooks", "common")
gitlab_hook_files = ['post-receive']
gitlab_hook_files.each do |file_name|
dest = File.join(gitolite_hooks_path, file_name)
print "#{dest} exists? ............"
if File.exists?(dest)
puts "YES".green
else
puts "NO".red
return
end
end
if Project.count > 0
puts "Validating projects repositories:".yellow
Project.find_each(:batch_size => 100) do |project|
......@@ -67,12 +81,6 @@ namespace :gitlab do
next
end
unless File.owned?(hook_file)
puts "post-receive file is not owner by gitlab".red
next
end
puts "post-reveice file ok".green
end
end
......
namespace :gitlab do
namespace :gitolite do
desc "GITLAB | Rewrite hooks for repos"
task :update_hooks => :environment do
puts "Starting Projects"
Project.find_each(:batch_size => 100) do |project|
begin
if project.commit
project.write_hooks
print ".".green
end
rescue Exception => e
print e.message.red
end
end
puts "\nDone with projects"
end
end
end
namespace :gitlab do
namespace :gitolite do
desc "GITLAB | Write GITLAB hook for gitolite"
task :write_hooks => :environment do
gitolite_hooks_path = File.join("/home", Gitlab.config.ssh_user, "share", "gitolite", "hooks", "common")
gitlab_hooks_path = Rails.root.join("lib", "hooks")
gitlab_hook_files = ['post-receive']
gitlab_hook_files.each do |file_name|
source = File.join(gitlab_hooks_path, file_name)
dest = File.join(gitolite_hooks_path, file_name)
puts "sudo -u root cp #{source} #{dest}".yellow
`sudo -u root cp #{source} #{dest}`
puts "sudo -u root chown git:git #{dest}".yellow
`sudo -u root chown git:git #{dest}`
end
end
end
end
require 'spec_helper'
describe ApplicationHelper do
describe "gravatar_icon" do
let(:user_email) { 'user@email.com' }
it "should return a generic avatar path when Gravatar is disabled" do
Gitlab.config.stub(:disable_gravatar?).and_return(true)
gravatar_icon(user_email).should == 'no_avatar.png'
end
it "should return a generic avatar path when email is blank" do
gravatar_icon('').should == 'no_avatar.png'
end
it "should use SSL when appropriate" do
stub!(:request).and_return(double(:ssl? => true))
gravatar_icon(user_email).should match('https://secure.gravatar.com')
end
it "should accept a custom size" do
stub!(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email, 64).should match(/\?s=64/)
end
end
end
......@@ -24,7 +24,7 @@ describe Notify do
end
it 'has the correct subject' do
should have_subject /Account was created for you/
should have_subject /^gitlab \| Account was created for you$/
end
it 'contains the new user\'s login name' do
......@@ -60,7 +60,7 @@ describe Notify do
it_behaves_like 'an assignee email'
it 'has the correct subject' do
should have_subject /new issue ##{issue.id}/
should have_subject /new issue ##{issue.id} \| #{issue.title} \| #{project.name}/
end
it 'contains a link to the new issue' do
......@@ -76,7 +76,7 @@ describe Notify do
it_behaves_like 'a multiple recipients email'
it 'has the correct subject' do
should have_subject /changed issue/
should have_subject /changed issue ##{issue.id} \| #{issue.title}/
end
it 'contains the name of the previous assignee' do
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment