Commit ed4fbcf6 authored by Alex Denisov's avatar Alex Denisov

Merge branch 'master' into fix_project_access_notification

parents 38b17e4e 5cb9e7ee
...@@ -22,4 +22,4 @@ config/unicorn.rb ...@@ -22,4 +22,4 @@ config/unicorn.rb
db/data.yml db/data.yml
.idea .idea
.DS_Store .DS_Store
.chef
...@@ -9,6 +9,8 @@ branches: ...@@ -9,6 +9,8 @@ branches:
- 'master' - 'master'
rvm: rvm:
- 1.9.3 - 1.9.3
services:
- mysql
before_script: before_script:
- "cp config/database.yml.$DB config/database.yml" - "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml" - "cp config/gitlab.yml.example config/gitlab.yml"
......
...@@ -323,7 +323,7 @@ GEM ...@@ -323,7 +323,7 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
rubyzip rubyzip
settingslogic (2.0.8) settingslogic (2.0.8)
shoulda-matchers (1.1.0) shoulda-matchers (1.3.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
simplecov (0.6.4) simplecov (0.6.4)
multi_json (~> 1.0) multi_json (~> 1.0)
......
$(document).ready(function(){
$('input#user_force_random_password').on('change', function(elem) {
var elems = $('#user_password, #user_password_confirmation');
if ($(this).attr('checked')) {
elems.val('').attr('disabled', true);
} else {
elems.removeAttr('disabled');
}
});
});
$ ->
$('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation')
if $(@).attr 'checked'
elems.val('').attr 'disabled', true
else
elems.removeAttr 'disabled'
...@@ -17,134 +17,3 @@ ...@@ -17,134 +17,3 @@
//= require raphael //= require raphael
//= require branch-graph //= require branch-graph
//= require_tree . //= require_tree .
$(document).ready(function(){
$(".one_click_select").live("click", function(){
$(this).select();
});
$('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
var buttons = $('[type="submit"]', this);
switch( e.type ){
case 'ajax:beforeSend':
case 'submit':
buttons.attr('disabled', 'disabled');
break;
case ' ajax:complete':
default:
buttons.removeAttr('disabled');
break;
}
})
$(".account-box").mouseenter(showMenu);
$(".account-box").mouseleave(resetMenu);
$("#projects-list .project").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
/**
* Focus search field by pressing 's' key
*/
$(document).keypress(function(e) {
if( $(e.target).is(":input") ) return;
switch(e.which) {
case 115: focusSearch();
e.preventDefault();
}
});
/**
* Commit show suppressed diff
*
*/
$(".supp_diff_link").bind("click", function() {
showDiff(this);
});
/**
* Note markdown preview
*
*/
$(document).on('click', '#preview-link', function(e) {
$('#preview-note').text('Loading...');
var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview');
$(this).text(previewLinkText);
var note = $('#note_note').val();
if (note.trim().length === 0) { note = 'Nothing to preview'; }
$.post($(this).attr('href'), {note: note}, function(data) {
$('#preview-note').html(data);
});
$('#preview-note, #note_note').toggle();
e.preventDefault();
});
});
function focusSearch() {
$("#search").focus();
}
function updatePage(data){
$.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
}
function showMenu() {
$(this).toggleClass('hover');
}
function resetMenu() {
$(this).removeClass("hover");
}
function slugify(text) {
return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
}
function showDiff(link) {
$(link).next('table').show();
$(link).remove();
}
(function($){
var _chosen = $.fn.chosen;
$.fn.extend({
chosen: function(options) {
var default_options = {'search_contains' : 'true'};
$.extend(default_options, options);
return _chosen.apply(this, [default_options]);
}})
})(jQuery);
function ajaxGet(url) {
$.ajax({type: "GET", url: url, dataType: "script"});
}
/**
* Disable button if text field is empty
*/
function disableButtonIfEmtpyField(field_selector, button_selector) {
field = $(field_selector);
if(field.val() == "") {
field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled");
}
field.on('keyup', function(){
var field = $(this);
var closest_submit = field.closest("form").find(button_selector);
if(field.val() == "") {
closest_submit.attr("disabled", "disabled").addClass("disabled");
} else {
closest_submit.removeAttr("disabled").removeClass("disabled");
}
})
}
function initGraphNav() {
$(".graph svg").css("position", "relative");
$("body").bind("keyup", function(e) {
if(e.keyCode == 37) { // left
$(".graph svg").animate({ left: "+=400" });
} else if(e.keyCode == 39) { // right
$(".graph svg").animate({ left: "-=400" });
}
});
}
initGraphNav = ->
$('.graph svg').css 'position', 'relative'
$('body').bind 'keyup', (e) ->
if e.keyCode is 37 # left
$('.graph svg').animate left: '+=400'
else if e.keyCode is 39 # right
$('.graph svg').animate left: '-=400'
window.initGraphNav = initGraphNav
var Loader = {
img_src: "/assets/ajax-loader.gif",
html:
function(width) {
img = $("<img>");
img.attr("width", width);
img.attr("src", this.img_src);
return img;
}
}
Loader =
html: (width) ->
$('<img>').attr src: '/assets/ajax-loader.gif', width: width
window.Loader = Loader
$(document).ready(function(){
$(".one_click_select").live("click", function(){
$(this).select();
});
$('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
var buttons = $('[type="submit"]', this);
switch( e.type ){
case 'ajax:beforeSend':
case 'submit':
buttons.attr('disabled', 'disabled');
break;
case ' ajax:complete':
default:
buttons.removeAttr('disabled');
break;
}
})
$(".account-box").mouseenter(showMenu);
$(".account-box").mouseleave(resetMenu);
$("#projects-list .project").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
/**
* Focus search field by pressing 's' key
*/
$(document).keypress(function(e) {
if( $(e.target).is(":input") ) return;
switch(e.which) {
case 115: focusSearch();
e.preventDefault();
}
});
/**
* Commit show suppressed diff
*
*/
$(".supp_diff_link").bind("click", function() {
showDiff(this);
});
/**
* Note markdown preview
*
*/
$(document).on('click', '#preview-link', function(e) {
$('#preview-note').text('Loading...');
var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview');
$(this).text(previewLinkText);
var note = $('#note_note').val();
if (note.trim().length === 0) { note = 'Nothing to preview'; }
$.post($(this).attr('href'), {note: note}, function(data) {
$('#preview-note').html(data);
});
$('#preview-note, #note_note').toggle();
e.preventDefault();
});
});
function focusSearch() {
$("#search").focus();
}
function updatePage(data){
$.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
}
function showMenu() {
$(this).toggleClass('hover');
}
function resetMenu() {
$(this).removeClass("hover");
}
function slugify(text) {
return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
}
function showDiff(link) {
$(link).next('table').show();
$(link).remove();
}
(function($){
var _chosen = $.fn.chosen;
$.fn.extend({
chosen: function(options) {
var default_options = {'search_contains' : 'true'};
$.extend(default_options, options);
return _chosen.apply(this, [default_options]);
}})
})(jQuery);
function ajaxGet(url) {
$.ajax({type: "GET", url: url, dataType: "script"});
}
/**
* Disable button if text field is empty
*/
function disableButtonIfEmtpyField(field_selector, button_selector) {
field = $(field_selector);
if(field.val() == "") {
field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled");
}
field.on('keyup', function(){
var field = $(this);
var closest_submit = field.closest("form").find(button_selector);
if(field.val() == "") {
closest_submit.attr("disabled", "disabled").addClass("disabled");
} else {
closest_submit.removeAttr("disabled").removeClass("disabled");
}
})
}
function Projects() {
$("#project_name").live("change", function(){
var slug = slugify($(this).val());
$("#project_code").val(slug);
$("#project_path").val(slug);
});
$('.new_project, .edit_project').live('ajax:before', function() {
$('.project_new_holder, .project_edit_holder').hide();
$('.save-project-loader').show();
});
$('form #project_default_branch').chosen();
disableButtonIfEmtpyField("#project_name", ".project-submit")
}
window.Projects = ->
$('#project_name').on 'change', ->
slug = slugify $(@).val()
$('#project_code, #project_path').val slug
$('.new_project, .edit_project').on 'ajax:before', ->
$('.project_new_holder, .project_edit_holder').hide()
$('.save-project-loader').show()
$('form #project_default_branch').chosen()
disableButtonIfEmtpyField '#project_name', '.project-submit'
# Git clone panel switcher
$ ->
scope = $ '.project_clone_holder'
if scope.length > 0
$('a, button', scope).click ->
$('a, button', scope).removeClass 'active'
$(@).addClass 'active'
$('#project_clone', scope).val $(@).data 'clone'
$(document).ready(function(){
$("#snippets-table .snippet").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
});
$ ->
$('#snippets-table .snippet').live 'click', (e) ->
if e.target.nodeName isnt 'A' and e.target.nodeName isnt 'INPUT'
location.href = $(@).attr 'url'
e.stopPropagation()
false
function backToMembers(){
$("#new_team_member").hide("slide", { direction: "right" }, 150, function(){
$("#team-table").show("slide", { direction: "left" }, 150, function() {
$("#new_team_member").remove();
$(".add_new").show();
});
});
}
/** /**
* =================================== * ===================================
* Contain 3 main UI block elements: * Contain 3 main UI block elements:
* .main_box - for show pages * .main_box - for show pages
* .ui-box - for simple block & widgets * .ui-box - for simple block & widgets
* =================================== * ===================================
......
.btn { .btn {
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #f7f7f7), to(#d5d5d5)); @include bg-gradient(#f7f7f7, #d5d5d5);
background-image: -webkit-linear-gradient(#f7f7f7 7.6%, #d5d5d5);
background-image: -moz-linear-gradient(#f7f7f7 7.6%, #d5d5d5);
background-image: -o-linear-gradient(#f7f7f7 7.6%, #d5d5d5);
border-color:#aaa; border-color:#aaa;
&:hover { &:hover {
@include bg-gray-gradient; @include bg-gray-gradient;
...@@ -12,10 +9,8 @@ ...@@ -12,10 +9,8 @@
&.primary { &.primary {
background:#2a79A3; background:#2a79A3;
@include bg-gradient(#47A7b7, #2585b5);
border-color: #2A79A3; border-color: #2A79A3;
background-image: -webkit-linear-gradient(#47A7b7 7.6%, #2585b5);
background-image: -moz-linear-gradient(#47A7b7 7.6%, #2585b5);
background-image: -o-linear-gradient(#47A7b7 7.6%, #2585b5);
color:#fff; color:#fff;
text-shadow: 0 1px 1px #268; text-shadow: 0 1px 1px #268;
&:hover { &:hover {
...@@ -30,16 +25,11 @@ ...@@ -30,16 +25,11 @@
} }
&.success { &.success {
border-color: #4A4; @extend .btn-success;
background-image: -webkit-linear-gradient(#82D482 7.6%, #22B442);
background-image: -moz-linear-gradient(#82D482 7.6%, #22B442);
background-image: -o-linear-gradient(#82D482 7.6%, #22B442);
color: #fff;
text-shadow: 0 1px 1px #141;
&:hover { &:hover {
background: #6C6; @extend .btn-success;
color: #fff; background: #51a351;
} }
&.disabled { &.disabled {
...@@ -62,10 +52,8 @@ ...@@ -62,10 +52,8 @@
padding-right:30px; padding-right:30px;
} }
&.danger, &.danger {
&.btn-danger { @extend .btn-danger;
color:#fff;
background: #DA4E49;
border-color: #BD362F; border-color: #BD362F;
&:hover { &:hover {
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
* *
*/ */
.file_holder { .file_holder {
border:1px solid #CCC; border:1px solid #BBB;
margin-bottom:1em; margin-bottom:1em;
@include solid_shade; @include solid_shade;
.file_title { .file_title {
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
@include bg-gray-gradient; @include bg-dark-gray-gradient;
margin: 0; margin: 0;
font-weight: normal; font-weight: normal;
font-weight: bold; font-weight: bold;
......
table.highlighttable table.highlighttable {
{
margin:0px; margin:0px;
padding:0px; padding:0px;
font-size:12px; font-size:12px;
table-layout:fixed; table-layout:fixed;
background: #EEE; background: #EEE;
box-shadow: none;
border: none;
td.linenos {
background:#eee;
border-left:none;
}
td.code {
border-right:none;
}
} }
td.code, td.code,
td.linenos{ td.linenos{
padding:0; padding:0;
......
@import "bootstrap"; @import "bootstrap";
@import "bootstrap-responsive"; @import "bootstrap-responsive";
/** GITLAB colors **/ /** GitLab colors **/
$link_color:#3A89A3; $link_color:#3A89A3;
$blue_link: #2fa0bb; $blue_link: #2fa0bb;
$style_color: #474d57; $style_color: #474d57;
$hover: #fdf5d9; $hover: #fdf5d9;
/** GITLAB Fonts **/ /** GitLab Fonts **/
@font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); } @font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); }
/** MIXINS **/ /** MIXINS **/
...@@ -56,6 +56,13 @@ $hover: #fdf5d9; ...@@ -56,6 +56,13 @@ $hover: #fdf5d9;
border-radius: $radius; border-radius: $radius;
} }
@mixin bg-gradient($from, $to) {
background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));
background-image: -webkit-linear-gradient($from, $to);
background-image: -moz-linear-gradient($from, $to);
background-image: -o-linear-gradient($from, $to);
}
@mixin bg-gray-gradient { @mixin bg-gray-gradient {
background:#eee; background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
...@@ -106,9 +113,9 @@ $hover: #fdf5d9; ...@@ -106,9 +113,9 @@ $hover: #fdf5d9;
@import "themes/ui_modern.scss"; @import "themes/ui_modern.scss";
/** /**
* Gitlab bootstrap. * GitLab bootstrap.
* Overrides some styles of twitter bootstrap. * Overrides some styles of twitter bootstrap.
* Also give some common classes for gitlab app * Also give some common classes for GitLab app
*/ */
@import "gitlab_bootstrap/common.scss"; @import "gitlab_bootstrap/common.scss";
@import "gitlab_bootstrap/typography.scss"; @import "gitlab_bootstrap/typography.scss";
......
...@@ -22,7 +22,7 @@ header { ...@@ -22,7 +22,7 @@ header {
* *
*/ */
.app_logo { .app_logo {
width:230px; width:200px;
float:left; float:left;
position:relative; position:relative;
top:-5px; top:-5px;
...@@ -31,7 +31,7 @@ header { ...@@ -31,7 +31,7 @@ header {
h1 { h1 {
padding-top: 5px; padding-top: 5px;
width:102px; width:90px;
background: url('logo_dark.png') no-repeat 0px -3px; background: url('logo_dark.png') no-repeat 0px -3px;
float:left; float:left;
margin-left:5px; margin-left:5px;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Main Menu of Application * Main Menu of Application
* *
*/ */
ul.main_menu { ul.main_menu {
border-radius: 4px; border-radius: 4px;
margin: auto; margin: auto;
margin:30px 0; margin:30px 0;
...@@ -12,7 +12,7 @@ ul.main_menu { ...@@ -12,7 +12,7 @@ ul.main_menu {
position:relative; position:relative;
overflow:hidden; overflow:hidden;
@include shade; @include shade;
.count { .count {
position: relative; position: relative;
top: -1px; top: -1px;
display: inline-block; display: inline-block;
...@@ -29,12 +29,12 @@ ul.main_menu { ...@@ -29,12 +29,12 @@ ul.main_menu {
border-radius: 8px; border-radius: 8px;
-moz-border-radius: 8px; -moz-border-radius: 8px;
} }
.label { .label {
background:$hover; background:$hover;
text-shadow:none; text-shadow:none;
color:$style_color; color:$style_color;
} }
li { li {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
display: table-cell; display: table-cell;
...@@ -43,7 +43,7 @@ ul.main_menu { ...@@ -43,7 +43,7 @@ ul.main_menu {
border-left: 1px solid #EEE; border-left: 1px solid #EEE;
border-bottom:2px solid #CFCFCF; border-bottom:2px solid #CFCFCF;
&:first-child{ &:first-child{
-webkit-border-top-left-radius: 4px; -webkit-border-top-left-radius: 4px;
-webkit-border-bottom-left-radius: 4px; -webkit-border-bottom-left-radius: 4px;
-moz-border-radius-topleft: 4px; -moz-border-radius-topleft: 4px;
...@@ -53,31 +53,31 @@ ul.main_menu { ...@@ -53,31 +53,31 @@ ul.main_menu {
border-left: 0; border-left: 0;
} }
&.current { &.current {
background-color:#D5D5D5; background-color:#D5D5D5;
border-bottom: 2px solid $style_color; border-bottom: 1px solid #AAA;
border-right: 1px solid #BBB; border-right: 1px solid #BBB;
border-left: 1px solid #BBB; border-left: 1px solid #BBB;
border-radius: 0 0 1px 1px; border-radius: 0 0 1px 1px;
&:first-child{ &:first-child{
border-bottom:none; border-bottom:none;
border-left:none; border-left:none;
} }
} }
&.home { &.home {
a { a {
background: url(home_icon.PNG) no-repeat center center; background: url(home_icon.PNG) no-repeat center center;
text-indent:-9999px; text-indent:-9999px;
min-width:20px; min-width:20px;
img { img {
position:relative; position:relative;
top:4px; top:4px;
} }
} }
} }
} }
a { a {
display: block; display: block;
text-align: center; text-align: center;
font-weight:bold; font-weight:bold;
......
...@@ -75,17 +75,21 @@ ...@@ -75,17 +75,21 @@
padding: 4px 7px; padding: 4px 7px;
border: 1px solid #CCC; border: 1px solid #CCC;
margin-bottom:5px; margin-bottom:5px;
input[type=text] { }
.project_clone_holder {
input[type="text"] {
border: 1px solid #BBB; border: 1px solid #BBB;
box-shadow: none;
} }
} }
.save-project-loader { .save-project-loader {
img { img {
margin-top:50px; margin-top:50px;
margin-bottom:50px; margin-bottom:50px;
} }
h3 { h3 {
@extend .page_title; @extend .page_title;
} }
......
...@@ -120,22 +120,12 @@ class ApplicationController < ActionController::Base ...@@ -120,22 +120,12 @@ class ApplicationController < ActionController::Base
end end
end end
def load_refs
if params[:ref].blank?
@branch = params[:branch].blank? ? nil : params[:branch]
@tag = params[:tag].blank? ? nil : params[:tag]
@ref = @branch || @tag || @project.try(:default_branch) || Repository.default_ref
else
@ref = params[:ref]
end
end
def render_404 def render_404
render file: File.join(Rails.root, "public", "404"), layout: false, status: "404" render file: File.join(Rails.root, "public", "404"), layout: false, status: "404"
end end
def require_non_empty_project def require_non_empty_project
redirect_to @project unless @project.repo_exists? && @project.has_commits? redirect_to @project if @project.empty_repo?
end end
def no_cache_headers def no_cache_headers
......
...@@ -59,12 +59,19 @@ class CommitsController < ApplicationController ...@@ -59,12 +59,19 @@ class CommitsController < ApplicationController
def patch def patch
@commit = project.commit(params[:id]) @commit = project.commit(params[:id])
send_data( send_data(
@commit.to_patch, @commit.to_patch,
type: "text/plain", type: "text/plain",
disposition: 'attachment', disposition: 'attachment',
filename: (@commit.id.to_s + ".patch") filename: "#{@commit.id.patch}"
) )
end end
protected
def load_refs
@ref ||= params[:ref].presence || params[:branch].presence || params[:tag].presence
@ref ||= @ref || @project.try(:default_branch) || 'master'
end
end end
...@@ -50,7 +50,7 @@ class ProjectsController < ApplicationController ...@@ -50,7 +50,7 @@ class ProjectsController < ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
if @project.repo_exists? && @project.has_commits? unless @project.empty_repo?
@last_push = current_user.recent_push(@project.id) @last_push = current_user.recent_push(@project.id)
render :show render :show
else else
......
module GitlabMarkdownHelper module GitlabMarkdownHelper
# Replaces references (i.e. @abc, #123, !456, ...) in the text with links to include Gitlab::Markdown
# 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 so they are not altered
# from http://github.github.com/github-flavored-markdown/
extractions = {}
text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
md5 = Digest::MD5.hexdigest(match)
extractions[md5] = match
"{gfm-extraction-#{md5}}"
end
# TODO: add popups with additional information
parser = Gitlab::Markdown.new(@project, html_options)
text = parser.parse(text)
# Insert pre block extractions
text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
extractions[$1]
end
sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class )
end
# Use this in places where you would normally use link_to(gfm(...), ...). # Use this in places where you would normally use link_to(gfm(...), ...).
# #
......
...@@ -111,18 +111,18 @@ class Notify < ActionMailer::Base ...@@ -111,18 +111,18 @@ class Notify < ActionMailer::Base
# Examples # Examples
# #
# >> subject('Lorem ipsum') # >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum" # => "GitLab | Lorem ipsum"
# #
# # Automatically inserts Project name when @project is set # # Automatically inserts Project name when @project is set
# >> @project = Project.last # >> @project = Project.last
# => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
# >> subject('Lorem ipsum') # >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum | Ruby on Rails" # => "GitLab | Lorem ipsum | Ruby on Rails"
# #
# # Accepts multiple arguments # # Accepts multiple arguments
# >> subject('Lorem ipsum', 'Dolor sit amet') # >> subject('Lorem ipsum', 'Dolor sit amet')
# => "gitlab | Lorem ipsum | Dolor sit amet" # => "GitLab | Lorem ipsum | Dolor sit amet"
def subject(*extra) def subject(*extra)
"gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") "GitLab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "")
end end
end end
...@@ -103,7 +103,7 @@ class Note < ActiveRecord::Base ...@@ -103,7 +103,7 @@ class Note < ActiveRecord::Base
# Returns true if this is an upvote note, # Returns true if this is an upvote note,
# otherwise false is returned # otherwise false is returned
def upvote? def upvote?
note =~ /^\+1/ ? true : false note.start_with?('+1') || note.start_with?(':+1:')
end end
end end
# == Schema Information # == Schema Information
......
...@@ -104,6 +104,8 @@ class Project < ActiveRecord::Base ...@@ -104,6 +104,8 @@ class Project < ActiveRecord::Base
length: { within: 1..255 } length: { within: 1..255 }
validates :owner, presence: true validates :owner, presence: true
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validate :check_limit validate :check_limit
validate :repo_name validate :repo_name
...@@ -187,7 +189,7 @@ end ...@@ -187,7 +189,7 @@ end
# private_flag :boolean(1) default(TRUE), not null # private_flag :boolean(1) default(TRUE), not null
# code :string(255) # code :string(255)
# owner_id :integer(4) # owner_id :integer(4)
# default_branch :string(255) default("master"), not null # default_branch :string(255)
# issues_enabled :boolean(1) default(TRUE), not null # issues_enabled :boolean(1) default(TRUE), not null
# wall_enabled :boolean(1) default(TRUE), not null # wall_enabled :boolean(1) default(TRUE), not null
# merge_requests_enabled :boolean(1) default(TRUE), not null # merge_requests_enabled :boolean(1) default(TRUE), not null
......
...@@ -14,7 +14,7 @@ class UsersProject < ActiveRecord::Base ...@@ -14,7 +14,7 @@ class UsersProject < ActiveRecord::Base
after_save :update_repository after_save :update_repository
after_destroy :update_repository after_destroy :update_repository
validates_uniqueness_of :user_id, scope: [:project_id] validates_uniqueness_of :user_id, scope: [:project_id], message: "already exists in project"
validates_presence_of :user_id validates_presence_of :user_id
validates_presence_of :project_id validates_presence_of :project_id
...@@ -48,10 +48,10 @@ class UsersProject < ActiveRecord::Base ...@@ -48,10 +48,10 @@ class UsersProject < ActiveRecord::Base
def self.access_roles def self.access_roles
{ {
"Guest" => GUEST, "Guest" => GUEST,
"Reporter" => REPORTER, "Reporter" => REPORTER,
"Developer" => DEVELOPER, "Developer" => DEVELOPER,
"Master" => MASTER "Master" => MASTER
} }
end end
......
module Account module Account
# Returns a string for use as a Gitolite user identifier
#
# Note that Gitolite 2.x requires the following pattern for users:
#
# ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$
def identifier def identifier
email.gsub /[^[:alnum:]]/, "_" # Replace non-word chars with underscores, then make sure it starts with
# valid chars
email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')
end end
def is_admin? def is_admin?
......
...@@ -16,7 +16,7 @@ module IssueCommonality ...@@ -16,7 +16,7 @@ module IssueCommonality
validates :title, validates :title,
presence: true, presence: true,
length: { within: 0..255 } length: { within: 0..255 }
validates :closed, inclusion: { in: [true, false] }
scope :opened, where(closed: false) scope :opened, where(closed: false)
scope :closed, where(closed: true) scope :closed, where(closed: true)
......
# Includes methods for handling Git Push events
#
# Triggered by PostReceive job
module PushObserver module PushObserver
def observe_push(oldrev, newrev, ref, user) def observe_push(oldrev, newrev, ref, user)
data = post_receive_data(oldrev, newrev, ref, user) data = post_receive_data(oldrev, newrev, ref, user)
...@@ -84,11 +87,10 @@ module PushObserver ...@@ -84,11 +87,10 @@ module PushObserver
data data
end end
# This method will be called after each post receive and only if the provided
# This method will be called after each post receive # user is present in GitLab.
# and only if user present in gitlab.
# All callbacks for post receive should be placed here
# #
# All callbacks for post receive should be placed here.
def trigger_post_receive(oldrev, newrev, ref, user) def trigger_post_receive(oldrev, newrev, ref, user)
# Create push event # Create push event
self.observe_push(oldrev, newrev, ref, user) self.observe_push(oldrev, newrev, ref, user)
...@@ -101,5 +103,11 @@ module PushObserver ...@@ -101,5 +103,11 @@ module PushObserver
# Create satellite # Create satellite
self.satellite.create unless self.satellite.exists? self.satellite.create unless self.satellite.exists?
# Discover the default branch, but only if it hasn't already been set to
# something else
if default_branch.nil?
update_attributes(default_branch: discover_default_branch)
end
end end
end end
...@@ -8,6 +8,10 @@ module Repository ...@@ -8,6 +8,10 @@ module Repository
false false
end end
def empty_repo?
!repo_exists? || !has_commits?
end
def commit(commit_id = nil) def commit(commit_id = nil)
Commit.find_or_first(repo, commit_id, root_ref) Commit.find_or_first(repo, commit_id, root_ref)
end end
...@@ -38,7 +42,7 @@ module Repository ...@@ -38,7 +42,7 @@ module Repository
def has_post_receive_file? def has_post_receive_file?
hook_file = File.join(path_to_repo, 'hooks', 'post-receive') hook_file = File.join(path_to_repo, 'hooks', 'post-receive')
File.exists?(hook_file) File.exists?(hook_file)
end end
def tags def tags
...@@ -67,7 +71,7 @@ module Repository ...@@ -67,7 +71,7 @@ module Repository
def repo_exists? def repo_exists?
@repo_exists ||= (repo && !repo.branches.empty?) @repo_exists ||= (repo && !repo.branches.empty?)
rescue rescue
@repo_exists = false @repo_exists = false
end end
...@@ -90,24 +94,42 @@ module Repository ...@@ -90,24 +94,42 @@ module Repository
end.sort_by(&:name) end.sort_by(&:name)
end end
# Discovers the default branch based on the repository's available branches
#
# - If no branches are present, returns nil
# - If one branch is present, returns its name
# - If two or more branches are present, returns the one that has a name
# matching root_ref (default_branch or 'master' if default_branch is nil)
def discover_default_branch
branches = heads.collect(&:name)
if branches.length == 0
nil
elsif branches.length == 1
branches.first
else
branches.select { |v| v == root_ref }.first
end
end
def has_commits? def has_commits?
!!commit !!commit
end end
def root_ref def root_ref
default_branch || "master" default_branch || "master"
end end
def root_ref? branch def root_ref?(branch)
root_ref == branch root_ref == branch
end end
# Archive Project to .tar.gz # Archive Project to .tar.gz
# #
# Already packed repo archives stored at # Already packed repo archives stored at
# app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
# #
def archive_repo ref def archive_repo(ref)
ref = ref || self.root_ref ref = ref || self.root_ref
commit = self.commit(ref) commit = self.commit(ref)
return nil unless commit return nil unless commit
...@@ -134,6 +156,6 @@ module Repository ...@@ -134,6 +156,6 @@ module Repository
end end
def http_url_to_repo def http_url_to_repo
http_url = [Gitlab.config.url, "/", path, ".git"].join() http_url = [Gitlab.config.url, "/", path, ".git"].join('')
end end
end end
...@@ -11,8 +11,13 @@ ...@@ -11,8 +11,13 @@
.input= f.text_field :title .input= f.text_field :title
.clearfix .clearfix
= f.label :key = f.label :key
.input= f.text_area :key, class: "xlarge" .input
= f.text_area :key, class: [:xxlarge, :thin_area]
%p.hint
Paste a machine public key here. Read more about how generate it
= link_to "here", help_ssh_path
.actions .actions
= f.submit 'Save', class: "primary btn" = f.submit 'Save', class: "save-btn btn"
= link_to "Cancel", project_deploy_keys_path(@project), class: "btn" = link_to "Cancel", project_deploy_keys_path(@project), class: "btn cancel-btn"
= render "repositories/head" = render "repositories/head"
- if can? current_user, :admin_project, @project
.alert-message.block-message %p.slead
Deploy keys allow read-only access to repository. Deploy keys allow read-only access to repository. It matches perfectly for CI, staging or production servers.
- if can? current_user, :admin_project, @project
= link_to new_project_deploy_key_path(@project), class: "btn small", title: "New Deploy Key" do = link_to new_project_deploy_key_path(@project), class: "btn small", title: "New Deploy Key" do
Add Deploy Key Add Deploy Key
- if @keys.any? - if @keys.any?
%table %table
%thead
%tr
%th Keys
%th
%th
- @keys.each do |key| - @keys.each do |key|
= render(partial: 'show', locals: {key: key}) = render(partial: 'show', locals: {key: key})
= render "repositories/head" = render "repositories/head"
%h3 New Deploy key %h3.page_title New Deploy key
%hr %hr
= render 'form' = render 'form'
= render "repositories/head" = render "repositories/head"
%h3= @key.title %h3.page_title
Deploy key:
= @key.title
%small
created at
= @key.created_at.stamp("Aug 21, 2011")
.back_link
= link_to project_deploy_keys_path(@project) do
&larr; To keys list
%hr %hr
%pre= @key.key %pre= @key.key
.actions .right
= link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key" = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key"
.clear
%h1 Git Error %h1 Git Error
%hr %hr
%h2 Gitlab was unable to access your Gitolite system. %h2 GitLab was unable to access your Gitolite system.
.git_error_tips .git_error_tips
%h4 Tips for Administrator: %h4 Tips for Administrator:
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
%a{href: "#users"} Users %a{href: "#users"} Users
%li %li
%a{href: "#issues"} Issues %a{href: "#issues"} Issues
%li
%a{href: "#milestones"} Milestones
.file_holder#README .file_holder#README
.file_title .file_title
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
%h5= link_to "API", help_api_path %h5= link_to "API", help_api_path
%li %li
%h5= link_to "Gitlab Markdown", help_markdown_path %h5= link_to "GitLab Markdown", help_markdown_path
%li %li
%h5= link_to "SSH keys", help_ssh_path %h5= link_to "SSH keys", help_ssh_path
%h3.page_title Gitlab Flavored Markdown %h3.page_title GitLab Flavored Markdown
.back_link .back_link
= link_to help_path do = link_to help_path do
&larr; to index &larr; to index
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.row .row
.span8 .span8
%p %p
For Gitlab we developed something we call "Gitlab Flavored Markdown" (GFM). For GitLab we developed something we call "GitLab Flavored Markdown" (GFM).
It extends the standard Markdown in a few significant ways adds some useful functionality. It extends the standard Markdown in a few significant ways adds some useful functionality.
%p You can use GFM in: %p You can use GFM in:
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
%p becomes %p becomes
= markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```} = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```}
%h4 Special Gitlab references %h4 Special GitLab references
%p %p
GFM recognizes special references. GFM recognizes special references.
...@@ -90,12 +90,11 @@ ...@@ -90,12 +90,11 @@
-# this example will only be shown if the user has a project with at least one issue -# this example will only be shown if the user has a project with at least one issue
- if @project = current_user.projects.first - if @project = current_user.projects.first
- if issue = @project.issues.first - if issue = @project.issues.first
%p For example in your #{link_to @project.name, project_path(@project)} project something like %p For example in your #{link_to @project.name, project_path(@project)} project, writing:
%pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." %pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it."
%p becomes %p becomes:
= markdown "This is related to ##{issue.id}. @#{current_user.name} is working on solving it." %pre= gfm "This is related to ##{issue.id}. @#{current_user.name} is working on solving it."
- @project = nil # Prevent this from bubbling up to page title
.span4.right .span4.right
.alert.alert-info .alert.alert-info
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%hr %hr
%p.slead %p.slead
SSH key allows you to establish a secure connection between your computer and Gitlab SSH key allows you to establish a secure connection between your computer and GitLab
%p.slead %p.slead
To generate a new SSH key just open your terminal and use code below. To generate a new SSH key just open your terminal and use code below.
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
\# Generating public/private rsa key pair... \# Generating public/private rsa key pair...
%p.slead %p.slead
Next just use code below to dump your public key and add to GITLAB SSH Keys Next just use code below to dump your public key and add to GitLab SSH Keys
%pre.dark %pre.dark
cat ~/.ssh/id_rsa.pub cat ~/.ssh/id_rsa.pub
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%hr %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. Your GitLab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
%br %br
System Hooks can be used for logging or change information in LDAP server. System Hooks can be used for logging or change information in LDAP server.
%br %br
......
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
%hr %hr
%p.slead %p.slead
Every Gitlab project can trigger a web server whenever the repo is pushed to. Every GitLab project can trigger a web server whenever the repo is pushed to.
%br %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. 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 %br
GITLAB will send POST request with commits information on every push. GitLab will send POST request with commits information on every push.
%h5 Hooks request example: %h5 Hooks request example:
= render "hooks/data_ex" = render "hooks/data_ex"
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
git commit -am "My feature is ready" git commit -am "My feature is ready"
%li %li
%p Push your branch to gitlabhq %p Push your branch to GitLab
.bash .bash
%pre.dark %pre.dark
git push origin $feature_name git push origin $feature_name
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
:timestamp => "2012-01-03T23:36:29+02:00", :timestamp => "2012-01-03T23:36:29+02:00",
:url => "http://localhost/diaspora/commits/da1560886d...", :url => "http://localhost/diaspora/commits/da1560886d...",
:author => { :author => {
:name => "gitlab dev user", :name => "GitLab dev user",
:email => "gitlabdev@dv6700.(none)" :email => "gitlabdev@dv6700.(none)"
} }
} }
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
= f.label :description, "Details" = f.label :description, "Details"
.input .input
= f.text_area :description, maxlength: 2000, class: "xxlarge", rows: 14 = f.text_area :description, maxlength: 2000, class: "xxlarge", rows: 14
%p.hint Issues are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.actions .actions
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%hr %hr
%p.slead %p.slead
SSH key allows you to establish a secure connection between your computer and Gitlab SSH key allows you to establish a secure connection between your computer and GitLab
%table#keys-table %table#keys-table
......
...@@ -10,5 +10,5 @@ ...@@ -10,5 +10,5 @@
%hr %hr
%pre= @key.key %pre= @key.key
.actions .right
= link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn danger delete-key" = link_to 'Remove', @key, confirm: 'Are you sure?', method: :delete, class: "btn danger delete-key"
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%head %head
%meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
%title %title
gitlabhq GitLab
:css :css
.header h1 {color: #BBBBBB !important; font: bold 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;} .header h1 {color: #BBBBBB !important; font: bold 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;}
.header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;} .header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;}
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
%td{align: "left", style: "padding: 18px 0 10px;", width: "580"} %td{align: "left", style: "padding: 18px 0 10px;", width: "580"}
%h1{style: "color: #BBBBBB; font: normal 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;"} %h1{style: "color: #BBBBBB; font: normal 32px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 40px;"}
gitlab GITLAB
- if @project - if @project
| #{@project.name} | #{@project.name}
%table{align: "center", bgcolor: "#fff", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background: #fff;", width: "600"} %table{align: "center", bgcolor: "#fff", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background: #fff;", width: "600"}
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
= f.label :description, "Description", class: "control-label" = f.label :description, "Description", class: "control-label"
.controls .controls
= f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10 = f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10
%p.hint Milestones are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. %p.hint Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.span6 .span6
.control-group .control-group
= f.label :due_date, "Due Date", class: "control-label" = f.label :due_date, "Due Date", class: "control-label"
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
= f.text_area :note, size: 255, class: 'note-text' = f.text_area :note, size: 255, class: 'note-text'
#preview-note.preview_note.hide #preview-note.preview_note.hide
.hint .hint
.right Comments are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.clearfix .clearfix
.row.note_advanced_opts.hide .row.note_advanced_opts.hide
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
Hi #{@user['name']}! Hi #{@user['name']}!
%p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "} %p{style: "color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif; "}
Administrator created account for you. Now you are a member of company gitlab application. Administrator created account for you. Now you are a member of company GitLab application.
%td{style: "font-size: 1px; line-height: 1px;", width: "21"} %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%tr %tr
%td{style: "font-size: 1px; line-height: 1px;", width: "21"} %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
......
...@@ -3,19 +3,19 @@ ...@@ -3,19 +3,19 @@
.span7 .span7
.form-horizontal .form-horizontal
.input-prepend.project_clone_holder .input-prepend.project_clone_holder
= link_to "SSH", "#", class: "btn small active", :"data-clone" => @project.ssh_url_to_repo %button{class: "btn small active", :"data-clone" => @project.ssh_url_to_repo} SSH
= link_to "HTTP", "#", class: "btn small", :"data-clone" => @project.http_url_to_repo %button{class: "btn small", :"data-clone" => @project.http_url_to_repo} HTTP
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5" = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5"
.span4.right .span4.right
.right .right
- if can? current_user, :download_code, @project - unless @project.empty_repo?
= link_to archive_project_repository_path(@project), class: "btn small grouped" do - if can? current_user, :download_code, @project
%i.icon-download-alt = link_to archive_project_repository_path(@project), class: "btn small grouped" do
Download %i.icon-download-alt
- if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project) Download
= link_to new_project_merge_request_path(@project), title: "New Merge Request", class: "btn small grouped" do - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
Merge Request = link_to new_project_merge_request_path(@project), title: "New Merge Request", class: "btn small grouped" do
- if @project.issues_enabled && can?(current_user, :write_issue, @project) Merge Request
= link_to new_project_issue_path(@project), title: "New Issue", class: "btn small grouped" do - if @project.issues_enabled && can?(current_user, :write_issue, @project)
Issue = link_to new_project_issue_path(@project), title: "New Issue", class: "btn small grouped" do
Issue
%h5.title
= @project.name
%br
%div
%a.btn.info{href: tree_project_ref_path(@project, @project.root_ref)} Browse code
&nbsp;
%a.btn{href: project_commits_path(@project)} Commits
%strong.right
= link_to project_path(@project) do
Switch to project &rarr;
%br
.alert-message.block-message.warning
.input
.input-prepend
%span.add-on git clone
= text_field_tag :project_clone, @project.url_to_repo, class: "xlarge one_click_select git_clone_url"
= simple_format @project.description
- unless @events.blank?
%h4.middle_title Recent Activity
.content_list= render @events
= render 'shared/no_ssh' = render 'shared/no_ssh'
.project_clone_panel = render 'clone_panel'
.row
.span7
.form-horizontal
.input-prepend.project_clone_holder
= link_to "SSH", "#", class: "btn small active", :"data-clone" => @project.ssh_url_to_repo
= link_to "HTTP", "#", class: "btn small", :"data-clone" => @project.http_url_to_repo
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5"
%div.git-empty %div.git-empty
%h4 Git global setup: %h4 Git global setup:
%pre.dark %pre.dark
...@@ -36,16 +30,3 @@ ...@@ -36,16 +30,3 @@
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
.prepend-top-20 .prepend-top-20
= link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right" = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right"
:javascript
$(function(){
var link_sel = ".project_clone_holder a";
$(link_sel).bind("click", function() {
$(link_sel).removeClass("active");
$(this).addClass("active");
$("#project_clone").val($(this).attr("data-clone"));
})
})
...@@ -2,13 +2,3 @@ ...@@ -2,13 +2,3 @@
= render 'clone_panel' = render 'clone_panel'
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
.content_list= render @events .content_list= render @events
:javascript
$(function(){
var link_sel = ".project_clone_holder a";
$(link_sel).bind("click", function() {
$(link_sel).removeClass("active");
$(this).addClass("active");
$("#project_clone").val($(this).attr("data-clone"));
})
})
...@@ -7,4 +7,8 @@ ...@@ -7,4 +7,8 @@
%li{class: "#{'active' if (controller.controller_name == "refs") }"} %li{class: "#{'active' if (controller.controller_name == "refs") }"}
= link_to tree_project_ref_path(@project, @ref) do = link_to tree_project_ref_path(@project, @ref) do
Source Source
%li.right
.input-prepend.project_clone_holder
%button{class: "btn small active", :"data-clone" => @project.ssh_url_to_repo} SSH
%button{class: "btn small", :"data-clone" => @project.http_url_to_repo} HTTP
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5"
...@@ -6,5 +6,5 @@ ...@@ -6,5 +6,5 @@
// Load last commit log for each file in tree // Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() { $('#tree-slider').waitForImages(function() {
ajaxGet('#{@logs_path}'); ajaxGet('#{@logs_path}');
}); });
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.team_member_show .team_member_show
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
= link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "right btn btn-danger" = link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "right btn danger"
.profile_avatar_holder .profile_avatar_holder
= image_tag gravatar_icon(user.email, 60), class: "borders" = image_tag gravatar_icon(user.email, 60), class: "borders"
%h3 %h3
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.middle_box_content .middle_box_content
.input .input
%span.cgray %span.cgray
Wiki content is parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}. Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
To link to a (new) page you can just type To link to a (new) page you can just type
%code [Link Title](page-slug) %code [Link Title](page-slug)
\. \.
......
...@@ -50,10 +50,6 @@ Gitlab::Application.routes.draw do ...@@ -50,10 +50,6 @@ Gitlab::Application.routes.draw do
end end
end end
resources :team_members, :only => [:edit, :update, :destroy] resources :team_members, :only => [:edit, :update, :destroy]
get 'mailer/preview_note'
get 'mailer/preview_user_new'
get 'mailer/preview_issue_new'
resources :hooks, :only => [:index, :create, :destroy] do resources :hooks, :only => [:index, :create, :destroy] do
get :test get :test
end end
......
class SetDefaultBranchDefaultToNil < ActiveRecord::Migration
def up
# Set the default_branch to allow nil, and default it to nil
change_column_null(:projects, :default_branch, true)
change_column_default(:projects, :default_branch, nil)
end
def down
change_column_null(:projects, :default_branch, false)
change_column_default(:projects, :default_branch, 'master')
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20120729131232) do ActiveRecord::Schema.define(:version => 20120905043334) do
create_table "events", :force => true do |t| create_table "events", :force => true do |t|
t.string "target_type" t.string "target_type"
...@@ -98,16 +98,16 @@ ActiveRecord::Schema.define(:version => 20120729131232) do ...@@ -98,16 +98,16 @@ ActiveRecord::Schema.define(:version => 20120729131232) do
t.string "name" t.string "name"
t.string "path" t.string "path"
t.text "description" t.text "description"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.boolean "private_flag", :default => true, :null => false t.boolean "private_flag", :default => true, :null => false
t.string "code" t.string "code"
t.integer "owner_id" t.integer "owner_id"
t.string "default_branch", :default => "master", :null => false t.string "default_branch"
t.boolean "issues_enabled", :default => true, :null => false t.boolean "issues_enabled", :default => true, :null => false
t.boolean "wall_enabled", :default => true, :null => false t.boolean "wall_enabled", :default => true, :null => false
t.boolean "merge_requests_enabled", :default => true, :null => false t.boolean "merge_requests_enabled", :default => true, :null => false
t.boolean "wiki_enabled", :default => true, :null => false t.boolean "wiki_enabled", :default => true, :null => false
end end
create_table "protected_branches", :force => true do |t| create_table "protected_branches", :force => true do |t|
......
# Gitlab API # GitLab API
All API requests require authentication. You need to pass `private_token` parameter to authenticate. All API requests require authentication. You need to pass a `private_token` parameter to authenticate. You can find or reset your private token in your profile.
To get or reset your token visit your profile. If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401:
If no or invalid `private_token` provided error message will be returned with status code 401:
```json ```json
{ {
...@@ -12,10 +10,9 @@ If no or invalid `private_token` provided error message will be returned with st ...@@ -12,10 +10,9 @@ If no or invalid `private_token` provided error message will be returned with st
} }
``` ```
API requests should be prefixed with `api` and the API version. API requests should be prefixed with `api` and the API version. The API version is equal to the GitLab major version number, which is defined in `lib/api.rb`.
API version is equal to Gitlab major version number and defined in `lib/api.rb`.
Example of valid API request: Example of a valid API request:
``` ```
GET http://example.com/api/v2/projects?private_token=QVy1PB7sTxfy4pqfZM1U GET http://example.com/api/v2/projects?private_token=QVy1PB7sTxfy4pqfZM1U
......
## List projects ## List projects
Get a list of authenticated user's projects. Get a list of projects owned by the authenticated user.
``` ```
GET /projects GET /projects
...@@ -55,7 +55,7 @@ GET /projects ...@@ -55,7 +55,7 @@ GET /projects
## Single project ## Single project
Get an authenticated user's project. Get a specific project, identified by project ID, which is owned by the authentication user.
``` ```
GET /projects/:id GET /projects/:id
...@@ -102,6 +102,12 @@ Parameters: ...@@ -102,6 +102,12 @@ Parameters:
+ `name` (required) - new project name + `name` (required) - new project name
+ `code` (optional) - new project code, uses project name if not set + `code` (optional) - new project code, uses project name if not set
+ `path` (optional) - new project path, uses project name if not set + `path` (optional) - new project path, uses project name if not set
+ `description (optional) - short project description
+ `default_branch` (optional) - 'master' by default
+ `issues_enabled` (optional) - enabled by default
+ `wall_enabled` (optional) - enabled by default
+ `merge_requests_enabled` (optional) - enabled by default
+ `wiki_enabled` (optional) - enabled by default
Will return created project with status `201 Created` on success, or `404 Not Will return created project with status `201 Created` on success, or `404 Not
found` on fail. found` on fail.
...@@ -109,7 +115,7 @@ found` on fail. ...@@ -109,7 +115,7 @@ found` on fail.
## Project repository branches ## Project repository branches
Get a list of project repository branches sorted by name alphabetically. Get a list of repository branches from a project, sorted by name alphabetically.
``` ```
GET /projects/:id/repository/branches GET /projects/:id/repository/branches
...@@ -186,7 +192,7 @@ Parameters: ...@@ -186,7 +192,7 @@ Parameters:
## Project repository tags ## Project repository tags
Get a list of project repository tags sorted by name in reverse alphabetical order. Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
``` ```
GET /projects/:id/repository/tags GET /projects/:id/repository/tags
...@@ -237,3 +243,4 @@ Parameters: ...@@ -237,3 +243,4 @@ Parameters:
+ `filepath` (required) - The path the file + `filepath` (required) - The path the file
Will return the raw file contents. Will return the raw file contents.
#!/bin/sh
sudo apt-get update
sudo apt-get upgrade
sudo DEBIAN_FRONTEND='noninteractive' apt-get install -y postfix-policyd-spf-python # Install postfix without prompting.
sudo apt-get install -y git git-core wget curl gcc checkinstall libxml2-dev libxslt-dev sqlite3 libsqlite3-dev libcurl4-openssl-dev libreadline-gplv2-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev libicu-dev redis-server openssh-server python-dev python-pip libyaml-dev
wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.gz
tar xfvz ruby-1.9.3-p194.tar.gz
cd ruby-1.9.3-p194
./configure
make
sudo make install
sudo adduser \
--system \
--shell /bin/sh \
--gecos 'git version control' \
--group \
--disabled-password \
--home /home/git \
git
sudo adduser --disabled-login --gecos 'gitlab system' gitlab
sudo usermod -a -G git gitlab
sudo -H -u gitlab ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa
cd /home/git
sudo -H -u git git clone git://github.com/gitlabhq/gitolite /home/git/gitolite
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; /home/git/gitolite/src/gl-system-install"
sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub
sudo chmod 777 /home/git/gitlab.pub
sudo -u git -H sed -i 's/0077/0007/g' /home/git/share/gitolite/conf/example.gitolite.rc
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gl-setup -q /home/git/gitlab.pub"
sudo chmod -R g+rwX /home/git/repositories/
sudo chown -R git:git /home/git/repositories/
sudo -u gitlab -H git clone git@localhost:gitolite-admin.git /tmp/gitolite-admin
sudo rm -rf /tmp/gitolite-admin
...@@ -20,7 +20,7 @@ You might have some luck using these, but no guarantees: ...@@ -20,7 +20,7 @@ You might have some luck using these, but no guarantees:
- MacOS X - MacOS X
- FreeBSD - FreeBSD
Gitlab does **not** run on Windows and we have no plans of making Gitlab compatible. GitLab does **not** run on Windows and we have no plans of making GitLab compatible.
## This installation guide created for Debian/Ubuntu and properly tested. ## This installation guide created for Debian/Ubuntu and properly tested.
...@@ -28,40 +28,44 @@ The installation consists of 6 steps: ...@@ -28,40 +28,44 @@ The installation consists of 6 steps:
1. Install packages / dependencies 1. Install packages / dependencies
2. Install ruby 2. Install ruby
3. Install gitolite 3. Install Gitolite
4. Install and configure Gitlab. 4. Install and configure GitLab.
5. Start the web front-end 5. Start the web front-end
6. Start a Resque worker (for background processing) 6. Start a Resque worker (for background processing)
### IMPORTANT ### IMPORTANT
Please make sure you have followed all the steps below before posting to the mailinglist with installation and configuration questions. Please make sure you have followed all the steps below before posting to the mailing list with installation and configuration questions.
Only create a Github Issue if you want a specific part of this installation guide updated. Only create a GitHub Issue if you want a specific part of this installation guide updated.
Also read the [Read this before you submit an issue](https://github.com/gitlabhq/gitlabhq/wiki/Read-this-before-you-submit-an-issue) wiki page. Also read the [Read this before you submit an issue](https://github.com/gitlabhq/gitlabhq/wiki/Read-this-before-you-submit-an-issue) wiki page.
> - - - > - - -
> First 3 steps can be easily skipped with simply install script: > The first 3 steps of this guide can be easily skipped by executing an install script:
> >
> # Install curl and sudo > # Install curl and sudo
> apt-get install curl sudo > apt-get install curl sudo
> >
> # 3 steps in 1 command :) > # 3 steps in 1 command :)
> curl https://raw.github.com/gitlabhq/gitlabhq/master/doc/debian_ubuntu.sh | sh > curl https://raw.github.com/gitlabhq/gitlab-recipes/master/install/debian_ubuntu.sh | sh
> >
> Now you can go to step 4" > Now you can go to [Step 4](#4-install-gitlab-and-configuration-check-status-configuration)
> >
> Or if you are installing on Amazon Web Services using Ubuntu 12.04 you can do all steps (1 to 6) at once with: > Or if you are installing on Amazon Web Services using Ubuntu 12.04 you can do all steps (1 to 6) at once with:
> >
> curl https://raw.github.com/gitlabhq/gitlabhq/master/lib/support/aws/debian_ubuntu_aws.sh | sh > curl https://raw.github.com/gitlabhq/gitlab-recipes/master/install/debian_ubuntu_aws.sh | sh
> >
> for more detailed instructions read the HOWTO section of [the script](https://github.com/gitlabhq/gitlabhq/blob/master/lib/support/aws/debian_ubuntu_aws.sh) > for more detailed instructions read the HOWTO section of [the script](https://github.com/gitlabhq/gitlab-recipes/blob/master/install/debian_ubuntu_aws.sh)
> - - - > - - -
# 1. Install packages # 1. Install packages
*Keep in mind that `sudo` is not installed for debian by default. You should install it with as root:* **apt-get update && apt-get upgrade && apt-get install sudo** *Keep in mind that `sudo` is not installed on Debian by default. You should install it as root:*
apt-get update && apt-get upgrade && apt-get install sudo
Now install the required packages:
sudo apt-get update sudo apt-get update
sudo apt-get upgrade sudo apt-get upgrade
...@@ -71,16 +75,16 @@ Also read the [Read this before you submit an issue](https://github.com/gitlabhq ...@@ -71,16 +75,16 @@ Also read the [Read this before you submit an issue](https://github.com/gitlabhq
# If you want to use MySQL: # If you want to use MySQL:
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
# 2. Install ruby # 2. Install Ruby
wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p290.tar.gz wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.gz
tar xzfv ruby-1.9.2-p290.tar.gz tar xfvz ruby-1.9.3-p194.tar.gz
cd ruby-1.9.2-p290 cd ruby-1.9.3-p194
./configure ./configure
make make
sudo make install sudo make install
# 3. Install gitolite # 3. Install Gitolite
Create user for git: Create user for git:
...@@ -93,12 +97,12 @@ Create user for git: ...@@ -93,12 +97,12 @@ Create user for git:
--home /home/git \ --home /home/git \
git git
Create user for gitlab: Create user for GitLab:
# ubuntu/debian # ubuntu/debian
sudo adduser --disabled-login --gecos 'gitlab system' gitlab sudo adduser --disabled-login --gecos 'gitlab system' gitlab
Add your user to git group: Add your user to the `git` group:
sudo usermod -a -G git gitlab sudo usermod -a -G git gitlab
...@@ -106,10 +110,10 @@ Generate key: ...@@ -106,10 +110,10 @@ Generate key:
sudo -H -u gitlab ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa sudo -H -u gitlab ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa
Get gitolite source code: Clone GitLab's fork of the Gitolite source code:
cd /home/git cd /home/git
sudo -H -u git git clone git://github.com/gitlabhq/gitolite /home/git/gitolite sudo -H -u git git clone https://github.com/gitlabhq/gitolite.git /home/git/gitolite
Setup: Setup:
...@@ -135,23 +139,23 @@ Permissions: ...@@ -135,23 +139,23 @@ Permissions:
# if succeed you can remove it # if succeed you can remove it
sudo rm -rf /tmp/gitolite-admin sudo rm -rf /tmp/gitolite-admin
**IMPORTANT! If you cant clone `gitolite-admin` repository - DONT PROCEED INSTALLATION** **IMPORTANT! If you can't clone `gitolite-admin` repository - DO NOT PROCEED WITH INSTALLATION**
Check the [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide)
and ensure you have followed all of the above steps carefully.
# 4. Install gitlab and configuration. Check status configuration. # 4. Clone GitLab source and install prerequisites
sudo gem install charlock_holmes --version '0.6.8' sudo gem install charlock_holmes --version '0.6.8'
sudo pip install pygments sudo pip install pygments
sudo gem install bundler sudo gem install bundler
cd /home/gitlab cd /home/gitlab
sudo -H -u gitlab git clone -b stable git://github.com/gitlabhq/gitlabhq.git gitlab sudo -H -u gitlab git clone -b stable https://github.com/gitlabhq/gitlabhq.git gitlab
cd gitlab cd gitlab
sudo -u gitlab mkdir tmp
# Rename config files # Rename config files
sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml
#### Select db you want to use #### Select the database you want to use
# SQLite # SQLite
sudo -u gitlab cp config/database.yml.sqlite config/database.yml sudo -u gitlab cp config/database.yml.sqlite config/database.yml
...@@ -163,7 +167,7 @@ Permissions: ...@@ -163,7 +167,7 @@ Permissions:
# Login to MySQL # Login to MySQL
$ mysql -u root -p $ mysql -u root -p
# Create the gitlabhq production database # Create the GitLab production database
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
# Create the MySQL User change $password to a real password # Create the MySQL User change $password to a real password
...@@ -179,15 +183,17 @@ Permissions: ...@@ -179,15 +183,17 @@ Permissions:
sudo -u gitlab -H bundle install --without development test --deployment sudo -u gitlab -H bundle install --without development test --deployment
#### Setup DB #### Setup database
sudo -u gitlab bundle exec rake gitlab:app:setup RAILS_ENV=production sudo -u gitlab bundle exec rake gitlab:app:setup RAILS_ENV=production
#### Setup gitlab hooks #### Setup GitLab hooks
sudo cp ./lib/hooks/post-receive /home/git/share/gitolite/hooks/common/post-receive 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 sudo chown git:git /home/git/share/gitolite/hooks/common/post-receive
#### Check application status
Checking status: Checking status:
sudo -u gitlab bundle exec rake gitlab:app:status RAILS_ENV=production sudo -u gitlab bundle exec rake gitlab:app:status RAILS_ENV=production
...@@ -208,9 +214,9 @@ Checking status: ...@@ -208,9 +214,9 @@ Checking status:
UMASK for .gitolite.rc is 0007? ............YES UMASK for .gitolite.rc is 0007? ............YES
/home/git/share/gitolite/hooks/common/post-receive exists? ............YES /home/git/share/gitolite/hooks/common/post-receive exists? ............YES
If you got all YES - congrats! You can go to next step. If you got all YES - congratulations! You can go to the next step.
# 5. Server up # 5. Start the web server
Application can be started with next command: Application can be started with next command:
...@@ -225,12 +231,12 @@ You can login via web using admin generated with setup: ...@@ -225,12 +231,12 @@ You can login via web using admin generated with setup:
admin@local.host admin@local.host
5iveL!fe 5iveL!fe
# 6. Run resque process (for processing queue). # 6. Run Resque process (for processing job queue).
# Manually # Manually
sudo -u gitlab bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production BACKGROUND=yes sudo -u gitlab bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production BACKGROUND=yes
# Gitlab start script # GitLab start script
sudo -u gitlab ./resque.sh sudo -u gitlab ./resque.sh
# if you run this as root /home/gitlab/gitlab/tmp/pids/resque_worker.pid will be owned by root # if you run this as root /home/gitlab/gitlab/tmp/pids/resque_worker.pid will be owned by root
# causing the resque worker not to start via init script on next boot/service restart # causing the resque worker not to start via init script on next boot/service restart
...@@ -240,19 +246,19 @@ You can login via web using admin generated with setup: ...@@ -240,19 +246,19 @@ You can login via web using admin generated with setup:
# Nginx && Unicorn # Nginx && Unicorn
### Install Nginx ## 1. Unicorn
sudo apt-get install nginx
## Unicorn
cd /home/gitlab/gitlab cd /home/gitlab/gitlab
sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb
sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D
Add gitlab to nginx sites & change with your host specific settings ## 2. Nginx
# Install first
sudo apt-get install nginx
sudo cp /home/gitlab/gitlab/lib/support/nginx-gitlab /etc/nginx/sites-available/gitlab # Add GitLab to nginx sites & change with your host specific settings
sudo wget https://raw.github.com/gitlabhq/gitlab-recipes/master/nginx/gitlab -P /etc/nginx/sites-available/
sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab
# Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN**
...@@ -260,22 +266,21 @@ Add gitlab to nginx sites & change with your host specific settings ...@@ -260,22 +266,21 @@ Add gitlab to nginx sites & change with your host specific settings
# of the host serving GitLab. # of the host serving GitLab.
sudo vim /etc/nginx/sites-enabled/gitlab sudo vim /etc/nginx/sites-enabled/gitlab
Restart nginx:
# Restart nginx:
/etc/init.d/nginx restart /etc/init.d/nginx restart
Create init script in /etc/init.d/gitlab: ## 3. Init script
cp /home/gitlab/gitlab/lib/support/init-gitlab /etc/init.d/gitlab
Adding permission: Create init script in /etc/init.d/gitlab:
sudo wget https://raw.github.com/gitlabhq/gitlab-recipes/master/init.d/gitlab -P /etc/init.d/
sudo chmod +x /etc/init.d/gitlab sudo chmod +x /etc/init.d/gitlab
Gitlab autostart: GitLab autostart:
sudo update-rc.d gitlab defaults sudo update-rc.d gitlab defaults
Now you can start/restart/stop gitlab like: Now you can start/restart/stop GitLab like:
sudo /etc/init.d/gitlab restart sudo /etc/init.d/gitlab restart
...@@ -4,7 +4,7 @@ Feature: Issues ...@@ -4,7 +4,7 @@ Feature: Issues
And I own project "Shop" And I own project "Shop"
And project "Shop" have "Release 0.4" open issue And project "Shop" have "Release 0.4" open issue
And project "Shop" have "Release 0.3" closed issue And project "Shop" have "Release 0.3" closed issue
And I visit project "Shop" issues page And I visit project "Shop" issues page
Scenario: I should see open issues Scenario: I should see open issues
Given I should see "Release 0.4" in issues Given I should see "Release 0.4" in issues
...@@ -36,3 +36,31 @@ Feature: Issues ...@@ -36,3 +36,31 @@ Feature: Issues
Given I visit issue page "Release 0.4" Given I visit issue page "Release 0.4"
And I leave a comment like "XML attached" And I leave a comment like "XML attached"
Then I should see comment "XML attached" Then I should see comment "XML attached"
@javascript
Scenario: I search issue
Given I fill in issue search with "Release"
Then I should see "Release 0.4" in issues
And I should not see "Release 0.3" in issues
@javascript
Scenario: I search issue that not exist
Given I fill in issue search with "Bug"
Then I should not see "Release 0.4" in issues
And I should not see "Release 0.3" in issues
@javascript
Scenario: I search all issues
Given I click link "All"
And I fill in issue search with "0.3"
Then I should see "Release 0.3" in issues
And I should not see "Release 0.4" in issues
@javascript
Scenario: I clear search
Given I click link "All"
And I fill in issue search with "Something"
And I fill in issue search with ""
Then I should see "Release 0.4" in issues
And I should see "Release 0.3" in issues
include LoginHelpers
Given /^I signin as a user$/ do
login_as :user
end
When /^I click link "(.*?)"$/ do |link|
click_link link
end
When /^I click button "(.*?)"$/ do |button|
click_button button
end
When /^I fill in "(.*?)" with "(.*?)"$/ do |field, value|
fill_in field, :with => value
end
Given /^show me page$/ do
save_and_open_page
end
Given /^I visit dashboard page$/ do
visit dashboard_path
end
Then /^I should see "(.*?)" link$/ do |arg1| Then /^I should see "(.*?)" link$/ do |arg1|
page.should have_link(arg1) page.should have_link(arg1)
end end
...@@ -51,10 +47,10 @@ Then /^I click "(.*?)" link$/ do |arg1| ...@@ -51,10 +47,10 @@ Then /^I click "(.*?)" link$/ do |arg1|
end end
Then /^I see prefilled new Merge Request page$/ do Then /^I see prefilled new Merge Request page$/ do
current_path.should == new_project_merge_request_path(@project) current_path.should == new_project_merge_request_path(@project)
find("#merge_request_source_branch").value.should == "new_design" find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master" find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New Design" find("#merge_request_title").value.should == "New Design"
end end
Given /^I visit dashboard search page$/ do Given /^I visit dashboard search page$/ do
...@@ -66,10 +62,6 @@ Given /^I search for "(.*?)"$/ do |arg1| ...@@ -66,10 +62,6 @@ Given /^I search for "(.*?)"$/ do |arg1|
click_button "Search" click_button "Search"
end end
Given /^I visit dashboard issues page$/ do
visit dashboard_issues_path
end
Then /^I should see issues assigned to me$/ do Then /^I should see issues assigned to me$/ do
issues = @user.issues issues = @user.issues
issues.each do |issue| issues.each do |issue|
...@@ -78,10 +70,6 @@ Then /^I should see issues assigned to me$/ do ...@@ -78,10 +70,6 @@ Then /^I should see issues assigned to me$/ do
end end
end end
Given /^I visit dashboard merge requests page$/ do
visit dashboard_merge_requests_path
end
Then /^I should see my merge requests$/ do Then /^I should see my merge requests$/ do
merge_requests = @user.merge_requests merge_requests = @user.merge_requests
merge_requests.each do |mr| merge_requests.each do |mr|
......
Given /^I visit profile page$/ do
visit profile_path
end
Then /^I should see my profile info$/ do Then /^I should see my profile info$/ do
page.should have_content "Profile" page.should have_content "Profile"
page.should have_content @user.name page.should have_content @user.name
page.should have_content @user.email page.should have_content @user.email
end end
Given /^I visit profile password page$/ do
visit profile_password_path
end
Then /^I change my password$/ do Then /^I change my password$/ do
fill_in "user_password", :with => "222333" fill_in "user_password", :with => "222333"
fill_in "user_password_confirmation", :with => "222333" fill_in "user_password_confirmation", :with => "222333"
...@@ -22,10 +14,6 @@ Then /^I should be redirected to sign in page$/ do ...@@ -22,10 +14,6 @@ Then /^I should be redirected to sign in page$/ do
current_path.should == new_user_session_path current_path.should == new_user_session_path
end end
Given /^I visit profile token page$/ do
visit profile_token_path
end
Then /^I reset my token$/ do Then /^I reset my token$/ do
@old_token = @user.private_token @old_token = @user.private_token
click_button "Reset" click_button "Reset"
......
Given /^I visit project source page$/ do
visit tree_project_ref_path(@project, @project.root_ref)
end
Then /^I should see files from repository$/ do Then /^I should see files from repository$/ do
page.should have_content("app") page.should have_content("app")
page.should have_content("History") page.should have_content("History")
page.should have_content("Gemfile") page.should have_content("Gemfile")
end end
Given /^I visit project source page for "(.*?)"$/ do |arg1|
visit tree_project_ref_path(@project, arg1)
end
Then /^I should see files from repository for "(.*?)"$/ do |arg1| Then /^I should see files from repository for "(.*?)"$/ do |arg1|
current_path.should == tree_project_ref_path(@project, arg1) current_path.should == tree_project_ref_path(@project, arg1)
page.should have_content("app") page.should have_content("app")
...@@ -31,10 +23,6 @@ Given /^I click on raw button$/ do ...@@ -31,10 +23,6 @@ Given /^I click on raw button$/ do
click_link "raw" click_link "raw"
end end
Given /^I visit blob file from repo$/ do
visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH)
end
Then /^I should see raw file content$/ do Then /^I should see raw file content$/ do
page.source.should == ValidCommit::BLOB_FILE page.source.should == ValidCommit::BLOB_FILE
end end
......
Given /^I visit project commits page$/ do
visit project_commits_path(@project)
end
Then /^I see project commits$/ do Then /^I see project commits$/ do
current_path.should == project_commits_path(@project) current_path.should == project_commits_path(@project)
...@@ -23,19 +19,11 @@ Then /^I see commits atom feed$/ do ...@@ -23,19 +19,11 @@ Then /^I see commits atom feed$/ do
page.body.should have_selector("entry summary", :text => commit.description) page.body.should have_selector("entry summary", :text => commit.description)
end end
Given /^I click on commit link$/ do
visit project_commit_path(@project, ValidCommit::ID)
end
Then /^I see commit info$/ do Then /^I see commit info$/ do
page.should have_content ValidCommit::MESSAGE page.should have_content ValidCommit::MESSAGE
page.should have_content "Showing 1 changed file" page.should have_content "Showing 1 changed file"
end end
Given /^I visit compare refs page$/ do
visit compare_project_commits_path(@project)
end
Given /^I fill compare fields with refs$/ do Given /^I fill compare fields with refs$/ do
fill_in "from", :with => "master" fill_in "from", :with => "master"
fill_in "to", :with => "stable" fill_in "to", :with => "stable"
...@@ -48,18 +36,6 @@ Given /^I see compared refs$/ do ...@@ -48,18 +36,6 @@ Given /^I see compared refs$/ do
page.should have_content "Showing 73 changed files" page.should have_content "Showing 73 changed files"
end end
Given /^I visit project branches page$/ do
visit branches_project_repository_path(@project)
end
Given /^I visit project commit page$/ do
visit project_commit_path(@project, ValidCommit::ID)
end
Given /^I visit project tags page$/ do
visit tags_project_repository_path(@project)
end
Then /^I should see "(.*?)" recent branches list$/ do |arg1| Then /^I should see "(.*?)" recent branches list$/ do |arg1|
page.should have_content("Branches") page.should have_content("Branches")
page.should have_content("master") page.should have_content("master")
...@@ -76,7 +52,7 @@ Then /^I should see "(.*?)" all tags list$/ do |arg1| ...@@ -76,7 +52,7 @@ Then /^I should see "(.*?)" all tags list$/ do |arg1|
end end
Then /^I should see "(.*?)" protected branches list$/ do |arg1| Then /^I should see "(.*?)" protected branches list$/ do |arg1|
within "table" do within "table" do
page.should have_content "stable" page.should have_content "stable"
page.should_not have_content "master" page.should_not have_content "master"
end end
......
...@@ -8,16 +8,12 @@ Given /^project "(.*?)" have "(.*?)" closed issue$/ do |arg1, arg2| ...@@ -8,16 +8,12 @@ Given /^project "(.*?)" have "(.*?)" closed issue$/ do |arg1, arg2|
Factory.create(:issue, :title => arg2, :project => project, :author => project.users.first, :closed => true) Factory.create(:issue, :title => arg2, :project => project, :author => project.users.first, :closed => true)
end end
Given /^I visit project "(.*?)" issues page$/ do |arg1|
visit project_issues_path(Project.find_by_name(arg1))
end
Given /^I should see "(.*?)" in issues$/ do |arg1| Given /^I should see "(.*?)" in issues$/ do |arg1|
page.should have_content arg1 page.should have_content arg1
end end
Given /^I should not see "(.*?)" in issues$/ do |arg1| Given /^I should not see "(.*?)" in issues$/ do |arg1|
page.should_not have_content arg1 page.should_not have_content arg1
end end
Then /^I should see issue "(.*?)"$/ do |arg1| Then /^I should see issue "(.*?)"$/ do |arg1|
...@@ -27,11 +23,6 @@ Then /^I should see issue "(.*?)"$/ do |arg1| ...@@ -27,11 +23,6 @@ Then /^I should see issue "(.*?)"$/ do |arg1|
page.should have_content issue.project.name page.should have_content issue.project.name
end end
Given /^I visit issue page "(.*?)"$/ do |arg1|
issue = Issue.find_by_title(arg1)
visit project_issue_path(issue.project, issue)
end
Given /^I submit new issue "(.*?)"$/ do |arg1| Given /^I submit new issue "(.*?)"$/ do |arg1|
fill_in "issue_title", with: arg1 fill_in "issue_title", with: arg1
click_button "Submit new issue" click_button "Submit new issue"
...@@ -51,7 +42,16 @@ Given /^I visit project "(.*?)" labels page$/ do |arg1| ...@@ -51,7 +42,16 @@ Given /^I visit project "(.*?)" labels page$/ do |arg1|
end end
Then /^I should see label "(.*?)"$/ do |arg1| Then /^I should see label "(.*?)"$/ do |arg1|
within ".labels-table" do within ".labels-table" do
page.should have_content arg1 page.should have_content arg1
end end
end end
Given /^I fill in issue search with "(.*?)"$/ do |arg1|
# Because fill_in, with: "" triggers nothing
# we need to trigger a keyup event
if arg1 == ''
page.execute_script("$('.issue_search').val('').keyup();");
end
fill_in 'issue_search', with: arg1
end
...@@ -8,21 +8,17 @@ Given /^project "(.*?)" have "(.*?)" closed merge request$/ do |arg1, arg2| ...@@ -8,21 +8,17 @@ Given /^project "(.*?)" have "(.*?)" closed merge request$/ do |arg1, arg2|
Factory.create(:merge_request, :title => arg2, :project => project, :author => project.users.first, :closed => true) Factory.create(:merge_request, :title => arg2, :project => project, :author => project.users.first, :closed => true)
end end
Given /^I visit project "(.*?)" merge requests page$/ do |arg1|
visit project_merge_requests_path(Project.find_by_name(arg1))
end
Then /^I should see "(.*?)" in merge requests$/ do |arg1| Then /^I should see "(.*?)" in merge requests$/ do |arg1|
page.should have_content arg1 page.should have_content arg1
end end
Then /^I should not see "(.*?)" in merge requests$/ do |arg1| Then /^I should not see "(.*?)" in merge requests$/ do |arg1|
page.should_not have_content arg1 page.should_not have_content arg1
end end
Then /^I should see merge request "(.*?)"$/ do |arg1| Then /^I should see merge request "(.*?)"$/ do |arg1|
merge_request = MergeRequest.find_by_title(arg1) merge_request = MergeRequest.find_by_title(arg1)
page.should have_content(merge_request.title[0..10]) page.should have_content(merge_request.title[0..10])
page.should have_content(merge_request.target_branch) page.should have_content(merge_request.target_branch)
page.should have_content(merge_request.source_branch) page.should have_content(merge_request.source_branch)
end end
...@@ -34,11 +30,6 @@ Given /^I submit new merge request "(.*?)"$/ do |arg1| ...@@ -34,11 +30,6 @@ Given /^I submit new merge request "(.*?)"$/ do |arg1|
click_button "Save" click_button "Save"
end end
Given /^I visit merge request page "(.*?)"$/ do |arg1|
mr = MergeRequest.find_by_title(arg1)
visit project_merge_request_path(mr.project, mr)
end
Then /^I should see closed merge request "(.*?)"$/ do |arg1| Then /^I should see closed merge request "(.*?)"$/ do |arg1|
mr = MergeRequest.find_by_title(arg1) mr = MergeRequest.find_by_title(arg1)
mr.closed.should be_true mr.closed.should be_true
......
...@@ -12,11 +12,6 @@ Given /^project "(.*?)" has milestone "(.*?)"$/ do |arg1, arg2| ...@@ -12,11 +12,6 @@ Given /^project "(.*?)" has milestone "(.*?)"$/ do |arg1, arg2|
end end
end end
Given /^I visit project "(.*?)" milestones page$/ do |arg1|
@project = Project.find_by_name(arg1)
visit project_milestones_path(@project)
end
Then /^I should see active milestones$/ do Then /^I should see active milestones$/ do
milestone = @project.milestones.first milestone = @project.milestones.first
page.should have_content(milestone.title[0..10]) page.should have_content(milestone.title[0..10])
......
...@@ -8,10 +8,6 @@ Given /^"(.*?)" is "(.*?)" developer$/ do |arg1, arg2| ...@@ -8,10 +8,6 @@ Given /^"(.*?)" is "(.*?)" developer$/ do |arg1, arg2|
project.add_access(user, :write) project.add_access(user, :write)
end end
Given /^I visit project "(.*?)" team page$/ do |arg1|
visit team_project_path(Project.find_by_name(arg1))
end
Then /^I should be able to see myself in team$/ do Then /^I should be able to see myself in team$/ do
page.should have_content(@user.name) page.should have_content(@user.name)
page.should have_content(@user.email) page.should have_content(@user.email)
...@@ -23,13 +19,9 @@ Then /^I should see "(.*?)" in team list$/ do |arg1| ...@@ -23,13 +19,9 @@ Then /^I should see "(.*?)" in team list$/ do |arg1|
page.should have_content(user.email) page.should have_content(user.email)
end end
Given /^I click link "(.*?)"$/ do |arg1|
click_link arg1
end
Given /^I select "(.*?)" as "(.*?)"$/ do |arg1, arg2| Given /^I select "(.*?)" as "(.*?)"$/ do |arg1, arg2|
user = User.find_by_name(arg1) user = User.find_by_name(arg1)
within "#new_team_member" do within "#new_team_member" do
select user.name, :from => "team_member_user_id" select user.name, :from => "team_member_user_id"
select arg2, :from => "team_member_project_access" select arg2, :from => "team_member_project_access"
end end
...@@ -44,7 +36,7 @@ end ...@@ -44,7 +36,7 @@ end
Given /^I change "(.*?)" role to "(.*?)"$/ do |arg1, arg2| Given /^I change "(.*?)" role to "(.*?)"$/ do |arg1, arg2|
user = User.find_by_name(arg1) user = User.find_by_name(arg1)
within ".user_#{user.id}" do within ".user_#{user.id}" do
select arg2, :from => "team_member_project_access" select arg2, :from => "team_member_project_access"
end end
end end
......
Given /^I visit project wiki page$/ do
visit project_wiki_path(@project, :index)
end
Given /^I create Wiki page$/ do Given /^I create Wiki page$/ do
fill_in "Title", :with => 'Test title' fill_in "Title", :with => 'Test title'
fill_in "Content", :with => '[link test](test)' fill_in "Content", :with => '[link test](test)'
......
include LoginHelpers
Given /^I signin as a user$/ do
login_as :user
end
When /^I visit new project page$/ do When /^I visit new project page$/ do
visit new_project_path visit new_project_path
end end
...@@ -65,10 +59,6 @@ Given /^I visit project "(.*?)" network page$/ do |arg1| ...@@ -65,10 +59,6 @@ Given /^I visit project "(.*?)" network page$/ do |arg1|
visit graph_project_path(project) visit graph_project_path(project)
end end
Given /^show me page$/ do
save_and_open_page
end
Given /^page should have network graph$/ do Given /^page should have network graph$/ do
page.should have_content "Project Network Graph" page.should have_content "Project Network Graph"
within ".graph" do within ".graph" do
......
Given /^I visit project "(.*?)" issues page$/ do |arg1|
visit project_issues_path(Project.find_by_name(arg1))
end
Given /^I visit issue page "(.*?)"$/ do |arg1|
issue = Issue.find_by_title(arg1)
visit project_issue_path(issue.project, issue)
end
Given /^I visit project "(.*?)" merge requests page$/ do |arg1|
visit project_merge_requests_path(Project.find_by_name(arg1))
end
Given /^I visit merge request page "(.*?)"$/ do |arg1|
mr = MergeRequest.find_by_title(arg1)
visit project_merge_request_path(mr.project, mr)
end
Given /^I visit project "(.*?)" milestones page$/ do |arg1|
@project = Project.find_by_name(arg1)
visit project_milestones_path(@project)
end
Given /^I visit project commits page$/ do
visit project_commits_path(@project)
end
Given /^I visit compare refs page$/ do
visit compare_project_commits_path(@project)
end
Given /^I visit project branches page$/ do
visit branches_project_repository_path(@project)
end
Given /^I visit project commit page$/ do
visit project_commit_path(@project, ValidCommit::ID)
end
Given /^I visit project tags page$/ do
visit tags_project_repository_path(@project)
end
Given /^I click on commit link$/ do
visit project_commit_path(@project, ValidCommit::ID)
end
Given /^I visit project source page$/ do
visit tree_project_ref_path(@project, @project.root_ref)
end
Given /^I visit project source page for "(.*?)"$/ do |arg1|
visit tree_project_ref_path(@project, arg1)
end
Given /^I visit blob file from repo$/ do
visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH)
end
Given /^I visit project "(.*?)" team page$/ do |arg1|
visit team_project_path(Project.find_by_name(arg1))
end
Given /^I visit project wiki page$/ do
visit project_wiki_path(@project, :index)
end
Given /^I visit profile page$/ do
visit profile_path
end
Given /^I visit profile token page$/ do
visit profile_token_path
end
Given /^I visit profile password page$/ do
visit profile_password_path
end
Given /^I visit dashboard page$/ do
visit dashboard_path
end
Given /^I visit dashboard issues page$/ do
visit dashboard_issues_path
end
Given /^I visit dashboard merge requests page$/ do
visit dashboard_merge_requests_path
end
...@@ -29,14 +29,24 @@ module Gitlab ...@@ -29,14 +29,24 @@ module Gitlab
# name (required) - name for new project # name (required) - name for new project
# code (optional) - code for new project, uses project name if not set # code (optional) - code for new project, uses project name if not set
# path (optional) - path for new project, uses project name if not set # path (optional) - path for new project, uses project name if not set
# description (optional) - short project description
# default_branch (optional) - 'master' by default
# issues_enabled (optional) - enabled by default
# wall_enabled (optional) - enabled by default
# merge_requests_enabled (optional) - enabled by default
# wiki_enabled (optional) - enabled by default
# Example Request # Example Request
# POST /projects # POST /projects
post do post do
project = {} params[:code] ||= params[:name]
project[:name] = params[:name] params[:path] ||= params[:name]
project[:code] = params[:code] || project[:name] project_attrs = {}
project[:path] = params[:path] || project[:name] params.each_pair do |k ,v|
@project = Project.create_by_user(project, current_user) if Project.attribute_names.include? k
project_attrs[k] = v
end
end
@project = Project.create_by_user(project_attrs, current_user)
if @project.saved? if @project.saved?
present @project, with: Entities::Project present @project, with: Entities::Project
else else
......
...@@ -170,7 +170,7 @@ module Gitlab ...@@ -170,7 +170,7 @@ module Gitlab
def push def push
Dir.chdir(File.join(@local_dir, "gitolite")) Dir.chdir(File.join(@local_dir, "gitolite"))
`git add -A` `git add -A`
`git commit -am "Gitlab"` `git commit -am "GitLab"`
`git push` `git push`
Dir.chdir(Rails.root) Dir.chdir(Rails.root)
......
module Gitlab module Gitlab
# Custom parser for Gitlab-flavored Markdown # Custom parser for GitLab-flavored Markdown
# #
# It replaces references in the text with links to the appropriate items in Gitlab. # It replaces references in the text with links to the appropriate items in
# GitLab.
# #
# Supported reference formats are: # Supported reference formats are:
# * @foo for team members # * @foo for team members
...@@ -10,19 +11,20 @@ module Gitlab ...@@ -10,19 +11,20 @@ module Gitlab
# * $123 for snippets # * $123 for snippets
# * 123456 for commits # * 123456 for commits
# #
# Examples # It also parses Emoji codes to insert images. See
# http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
# #
# >> m = Markdown.new(...) # Examples
# #
# >> m.parse("Hey @david, can you fix this?") # >> gfm("Hey @david, can you fix this?")
# => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?" # => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
# #
# >> m.parse("Commit 35d5f7c closes #1234") # >> gfm("Commit 35d5f7c closes #1234")
# => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>" # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
class Markdown #
include Rails.application.routes.url_helpers # >> gfm(":trollface:")
include ActionView::Helpers # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
module Markdown
REFERENCE_PATTERN = %r{ REFERENCE_PATTERN = %r{
([^\w&;])? # Prefix (1) ([^\w&;])? # Prefix (1)
( # Reference (2) ( # Reference (2)
...@@ -33,15 +35,52 @@ module Gitlab ...@@ -33,15 +35,52 @@ module Gitlab
([^\w&;])? # Suffix (6) ([^\w&;])? # Suffix (6)
}x.freeze }x.freeze
EMOJI_PATTERN = %r{(:(\S+):)}.freeze
attr_reader :html_options attr_reader :html_options
def initialize(project, html_options = {}) # Public: Parse the provided text with GitLab-Flavored Markdown
@project = project #
# 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
def gfm(text, html_options = {})
return text if text.nil?
return text if @project.nil?
@html_options = html_options @html_options = html_options
# 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|
md5 = Digest::MD5.hexdigest(match)
extractions[md5] = match
"{gfm-extraction-#{md5}}"
end
# TODO: add popups with additional information
text = parse(text)
# Insert pre block extractions
text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
extractions[$1]
end
sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class)
end end
private
# Private: Parses text for references and emoji
#
# text - Text to parse
#
# Returns parsed text
def parse(text) def parse(text)
text.gsub(REFERENCE_PATTERN) do |match| text = text.gsub(REFERENCE_PATTERN) do |match|
prefix = $1 || '' prefix = $1 || ''
reference = $2 reference = $2
identifier = $3 || $4 || $5 identifier = $3 || $4 || $5
...@@ -53,9 +92,26 @@ module Gitlab ...@@ -53,9 +92,26 @@ module Gitlab
match match
end end
end end
text = text.gsub(EMOJI_PATTERN) do |match|
if valid_emoji?($2)
image_tag("emoji/#{$2}.png", size: "20x20", class: 'emoji', title: $1, alt: $1)
else
match
end
end
text
end end
private # Private: Checks if an emoji icon exists in the image asset directory
#
# emoji - Identifier of the emoji as a string (e.g., "+1", "heart")
#
# Returns boolean
def valid_emoji?(emoji)
File.exists?(Rails.root.join('app', 'assets', 'images', 'emoji', "#{emoji}.png"))
end
# Private: Dispatches to a dedicated processing method based on reference # Private: Dispatches to a dedicated processing method based on reference
# #
......
#!/usr/bin/env bash #!/usr/bin/env bash
# This file was placed here by Gitlab. It makes sure that your pushed commits # This file was placed here by GitLab. It makes sure that your pushed commits
# will be processed properly. # will be processed properly.
while read oldrev newrev ref while read oldrev newrev ref
......
#!/bin/sh
# ABOUT
# This script performs a complete installation of Gitlab (master branch).
# Is can be run with one command without needing _any_ user input after that.
# This script only works on Amazon Web Services (AWS).
# The operating system used is Ubuntu 12.04 64bit.
# HOWTO
# Signup for AWS, free tier are available at http://aws.amazon.com/free/
# Go to EC2 tab in the AWS console EC2 https://console.aws.amazon.com/ec2/home
# Click the 'Launch Instance' button
# Select: 'Quick launch wizard' and continue
# Choose a key pair => Create New => Name it => Download it
# Choose a Launch Configuration => Select 'More Amazon Marketplace Images'
# Press 'Continue'
# Enter 'ubuntu/images/ubuntu-precise-12.04-amd64-server-20120424' and press 'Search'
# Select the only result (ami-3c994355) and press 'Continue'
# Press 'Edit details' if you want to modify something, for example make the type 'c1.medium' to make the install faster.
# Press the 'Launch' button
# Press 'Close'
# Click 'Security Groups' under the left hand menu 'NETWORK & SECURITY'
# Select the newly create seciruty group, probably named 'quicklaunch-1'
# Click on the Inbound tab
# In the 'Create a new rule' dropdown select 'HTTP'
# Press 'Add Rule'
# In the 'Create a new rule' dropdown select 'HTTPS'
# Press 'Add Rule'
# Press 'Apply Rule Changes'
# Give the following command in your local terminal while suptituting the UPPERCASE items
# 'ssh -i LOCATION_OF_AWS_KEY_PAIR_PRIVATE_KEY PUBLIC_DNS_OF_THE_NEW_SERVER'
# Execute the curl command below and when its ready follow the printed 'Log in instuctions'
# curl https://raw.github.com/gitlabhq/gitlabhq/master/lib/support/aws/debian_ubuntu_aws.sh | sh
# Prevent fingerprint prompt for localhost in step 1 to 3.
echo "Host localhost
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null" | sudo tee -a /etc/ssh/ssh_config
# Existing script for Step 1 to 3
curl https://raw.github.com/gitlabhq/gitlabhq/master/doc/debian_ubuntu.sh | sh
# Install MySQL
sudo apt-get install -y makepasswd # Needed to create a unique password non-interactively.
userPassword=$(makepasswd --char=10) # Generate a random MySQL password
# Note that the lines below creates a cleartext copy of the random password in /var/cache/debconf/passwords.dat
# This file is normally only readable by root and the password will be deleted by the package management system after install.
echo mysql-server mysql-server/root_password password $userPassword | sudo debconf-set-selections
echo mysql-server mysql-server/root_password_again password $userPassword | sudo debconf-set-selections
sudo apt-get install -y mysql-server
# Gitlab install
sudo gem install charlock_holmes --version '0.6.8'
sudo pip install pygments
sudo gem install bundler
sudo su -l gitlab -c "git clone git://github.com/gitlabhq/gitlabhq.git gitlab" # Using master everywhere.
sudo su -l gitlab -c "cd gitlab && mkdir tmp"
sudo su -l gitlab -c "cd gitlab/config && cp gitlab.yml.example gitlab.yml"
sudo su -l gitlab -c "cd gitlab/config && cp database.yml.example database.yml"
sudo sed -i 's/"secure password"/"'$userPassword'"/' /home/gitlab/gitlab/config/database.yml # Insert the mysql root password.
sudo su -l gitlab -c "cd gitlab && bundle install --without development test --deployment"
sudo su -l gitlab -c "cd gitlab && bundle exec rake gitlab:app:setup RAILS_ENV=production"
# Setup gitlab hooks
sudo cp /home/gitlab/gitlab/lib/hooks/post-receive /home/git/share/gitolite/hooks/common/post-receive
sudo chown git:git /home/git/share/gitolite/hooks/common/post-receive
# Set the first occurrence of host in the Gitlab config to the publicly available domain name
sudo sed -i '0,/host/s/localhost/'`wget -qO- http://instance-data/latest/meta-data/public-hostname`'/' /home/gitlab/gitlab/config/gitlab.yml
# Gitlab installation test (optional)
# sudo -u gitlab bundle exec rake gitlab:app:status RAILS_ENV=production
# sudo -u gitlab bundle exec rails s -e production
# sudo -u gitlab bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production BACKGROUND=no
# Install and configure Nginx
sudo apt-get install -y nginx
sudo cp /home/gitlab/gitlab/lib/support/nginx-gitlab /etc/nginx/sites-available/gitlab
sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab
sudo sed -i 's/YOUR_SERVER_IP/'`wget -qO- http://instance-data/latest/meta-data/local-ipv4`'/' /etc/nginx/sites-available/gitlab # Set private ip address (public won't work).
sudo sed -i 's/YOUR_SERVER_FQDN/'`wget -qO- http://instance-data/latest/meta-data/public-hostname`'/' /etc/nginx/sites-available/gitlab # Set public dns domain name.
# Configure Unicorn
sudo -u gitlab cp /home/gitlab/gitlab/config/unicorn.rb.orig /home/gitlab/gitlab/config/unicorn.rb
# Create a Gitlab service
sudo cp /home/gitlab/gitlab/lib/support/init-gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab && sudo update-rc.d gitlab defaults
## Gitlab service commands (unicorn and resque)
## restart doesn't restart resque, only start/stop effect it.
sudo -u gitlab service gitlab start
# sudo -u gitlab service gitlab restart
# sudo -u gitlab service gitlab stop
# nginx Service commands
# sudo service nginx start
sudo service nginx restart
# sudo service nginx stop
# Manual startup commands for troubleshooting when the service commands do not work
# sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D
# sudo su -l gitlab -c "cd gitlab && ./resque.sh"
# Monitoring commands
# sudo tail -f /var/log/nginx/access.log;
# sudo tail -f /var/log/nginx/error.log;
# Go to gitlab directory by default on next login.
echo 'cd /home/gitlab/gitlab' >> /home/ubuntu/.bashrc
echo ''
echo '###########################################'
echo '# Log in instuctions #'
echo '###########################################'
echo ''
echo "Surf to this Gitlab installation in your browser:"
echo "http://`wget -qO- http://instance-data/latest/meta-data/public-hostname`/"
echo ''
echo 'and login with the following Email and Password:'
echo 'admin@local.host'
echo '5iveL!fe'
\ No newline at end of file
#! /bin/bash
### BEGIN INIT INFO
# Provides: gitlab
# Required-Start: $local_fs $remote_fs $network $syslog redis-server
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: GitLab git repository management
# Description: GitLab git repository management
### END INIT INFO
DAEMON_OPTS="-c /home/gitlab/gitlab/config/unicorn.rb -E production -D"
NAME=unicorn
DESC="Gitlab service"
PID=/home/gitlab/gitlab/tmp/pids/unicorn.pid
RESQUE_PID=/home/gitlab/gitlab/tmp/pids/resque_worker.pid
case "$1" in
start)
CD_TO_APP_DIR="cd /home/gitlab/gitlab"
START_DAEMON_PROCESS="bundle exec unicorn_rails $DAEMON_OPTS"
START_RESQUE_PROCESS="./resque.sh"
echo -n "Starting $DESC: "
if [ `whoami` = root ]; then
sudo -u gitlab sh -l -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS"
else
$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS
fi
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
kill -QUIT `cat $PID`
kill -QUIT `cat $RESQUE_PID`
echo "$NAME."
;;
restart)
echo -n "Restarting $DESC: "
kill -USR2 `cat $PID`
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration: "
kill -HUP `cat $PID`
echo "$NAME."
;;
*)
echo "Usage: $NAME {start|stop|restart|reload}" >&2
exit 1
;;
esac
exit 0
upstream gitlab {
server unix:/home/gitlab/gitlab/tmp/sockets/gitlab.socket;
}
server {
listen YOUR_SERVER_IP:80; # e.g., listen 192.168.1.1:80;
server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com;
root /home/gitlab/gitlab/public;
# individual nginx logs for this gitlab vhost
access_log /var/log/nginx/gitlab_access.log;
error_log /var/log/nginx/gitlab_error.log;
location / {
# serve static files from defined root folder;.
# @gitlab is a named location for the upstream fallback, see below
try_files $uri $uri/index.html $uri.html @gitlab;
}
# if a file, which is not found in the root folder is requested,
# then the proxy pass the request to the upsteam (gitlab unicorn)
location @gitlab {
proxy_redirect off;
# you need to change this to "https", if you set "ssl" directive to "on"
proxy_set_header X-FORWARDED_PROTO http;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://gitlab;
}
}
IMPORT_DIRECTORY = 'import_projects' IMPORT_DIRECTORY = 'import_projects'
REPOSITORY_DIRECTORY = '/home/git/repositories'
desc "Imports existing Git repos into new projects from the import_projects folder" desc "Imports existing Git repos into new projects from the import_projects folder"
task :import_projects, [:email] => :environment do |t, args| task :import_projects, [:email] => :environment do |t, args|
REPOSITORY_DIRECTORY = Gitlab.config.git_base_path
user_email = args.email user_email = args.email
repos_to_import = Dir.glob("#{IMPORT_DIRECTORY}/*") repos_to_import = Dir.glob("#{IMPORT_DIRECTORY}/*")
......
namespace :gitlab do namespace :gitlab do
namespace :gitolite do namespace :gitolite do
desc "GITLAB | Write GITLAB hook for gitolite" desc "GITLAB | Write GitLab hook for gitolite"
task :write_hooks => :environment do task :write_hooks => :environment do
gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common")
gitlab_hooks_path = Rails.root.join("lib", "hooks") gitlab_hooks_path = Rails.root.join("lib", "hooks")
......
...@@ -11,6 +11,9 @@ module Factory ...@@ -11,6 +11,9 @@ module Factory
def self.new(type, *args) def self.new(type, *args)
FactoryGirl.build(type, *args) FactoryGirl.build(type, *args)
end end
def self.attributes(type, *args)
FactoryGirl.attributes_for(type, *args)
end
end end
FactoryGirl.define do FactoryGirl.define do
...@@ -28,7 +31,7 @@ FactoryGirl.define do ...@@ -28,7 +31,7 @@ FactoryGirl.define do
email { Faker::Internet.email } email { Faker::Internet.email }
name name
password "123456" password "123456"
password_confirmation "123456" password_confirmation { password }
trait :admin do trait :admin do
admin true admin true
......
require 'spec_helper' require 'spec_helper'
describe "Factories" do FactoryGirl.factories.map(&:name).each do |factory_name|
describe 'User' do describe "#{factory_name} factory" do
it "builds a valid instance" do it 'should be valid' do
build(:user).should be_valid build(factory_name).should be_valid
end
it "builds a valid admin instance" do
build(:admin).should be_valid
end
end
describe 'Project' do
it "builds a valid instance" do
build(:project).should be_valid
end
end
describe 'Issue' do
it "builds a valid instance" do
build(:issue).should be_valid
end
it "builds a valid closed instance" do
build(:closed_issue).should be_valid
end
end
describe 'MergeRequest' do
it "builds a valid instance" do
build(:merge_request).should be_valid
end
end
describe 'Note' do
it "builds a valid instance" do
build(:note).should be_valid
end
end
describe 'Event' do
it "builds a valid instance" do
build(:event).should be_valid
end
end
describe 'Key' do
it "builds a valid instance" do
build(:key).should be_valid
end
it "builds a valid deploy key instance" do
build(:deploy_key).should be_valid
end
it "builds a valid personal key instance" do
build(:personal_key).should be_valid
end
end
describe 'Milestone' do
it "builds a valid instance" do
build(:milestone).should be_valid
end
end
describe 'SystemHook' do
it "builds a valid instance" do
build(:system_hook).should be_valid
end
end
describe 'ProjectHook' do
it "builds a valid instance" do
build(:project_hook).should be_valid
end
end
describe 'Wiki' do
it "builds a valid instance" do
build(:wiki).should be_valid
end
end
describe 'Snippet' do
it "builds a valid instance" do
build(:snippet).should be_valid
end end
end end
end end
...@@ -208,6 +208,46 @@ describe GitlabMarkdownHelper do ...@@ -208,6 +208,46 @@ describe GitlabMarkdownHelper do
gfm(actual).should match(expected) gfm(actual).should match(expected)
end end
end end
describe "emoji" do
it "matches at the start of a string" do
gfm(":+1:").should match(/<img/)
end
it "matches at the end of a string" do
gfm("This gets a :-1:").should match(/<img/)
end
it "matches with adjacent text" do
gfm("+1 (:+1:)").should match(/<img/)
end
it "has a title attribute" do
gfm(":-1:").should match(/title=":-1:"/)
end
it "has an alt attribute" do
gfm(":-1:").should match(/alt=":-1:"/)
end
it "has an emoji class" do
gfm(":+1:").should match('class="emoji"')
end
it "sets height and width" do
actual = gfm(":+1:")
actual.should match(/width="20"/)
actual.should match(/height="20"/)
end
it "keeps whitespace intact" do
gfm("This deserves a :+1: big time.").should match(/deserves a <img.+\/> big time/)
end
it "ignores invalid emoji" do
gfm(":invalid-emoji:").should_not match(/<img/)
end
end
end end
describe "#link_to_gfm" do describe "#link_to_gfm" do
......
...@@ -24,7 +24,7 @@ describe Notify do ...@@ -24,7 +24,7 @@ describe Notify do
end end
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /^gitlab \| Account was created for you$/ should have_subject /^gitlab \| Account was created for you$/i
end end
it 'contains the new user\'s login name' do it 'contains the new user\'s login name' do
......
...@@ -7,6 +7,7 @@ describe Issue do ...@@ -7,6 +7,7 @@ describe Issue do
describe "Validation" do describe "Validation" do
it { should ensure_length_of(:description).is_within(0..2000) } it { should ensure_length_of(:description).is_within(0..2000) }
it { should ensure_inclusion_of(:closed).in_array([true, false]) }
end end
describe 'modules' do describe 'modules' do
......
...@@ -9,6 +9,7 @@ describe Milestone do ...@@ -9,6 +9,7 @@ describe Milestone do
describe "Validation" do describe "Validation" do
it { should validate_presence_of(:title) } it { should validate_presence_of(:title) }
it { should validate_presence_of(:project_id) } it { should validate_presence_of(:project_id) }
it { should ensure_inclusion_of(:closed).in_array([true, false]) }
end end
let(:milestone) { Factory :milestone } let(:milestone) { Factory :milestone }
......
...@@ -35,6 +35,16 @@ describe Note do ...@@ -35,6 +35,16 @@ describe Note do
note = Factory(:note, note: "-1 for this") note = Factory(:note, note: "-1 for this")
note.should_not be_upvote note.should_not be_upvote
end end
it "recognizes a +1 emoji as a vote" do
note = build(:note, note: ":+1: for this")
note.should be_upvote
end
it "recognizes a neutral emoji note" do
note = build(:note, note: "I would :+1: this, but I don't want to")
note.should_not be_upvote
end
end end
let(:project) { create(:project) } let(:project) { create(:project) }
......
...@@ -37,6 +37,10 @@ describe Project do ...@@ -37,6 +37,10 @@ describe Project do
# TODO: Formats # TODO: Formats
it { should validate_presence_of(:owner) } it { should validate_presence_of(:owner) }
it { should ensure_inclusion_of(:issues_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) }
it "should not allow new projects beyond user limits" do it "should not allow new projects beyond user limits" do
project.stub(:owner).and_return(double(can_create_project?: false, projects_limit: 1)) project.stub(:owner).and_return(double(can_create_project?: false, projects_limit: 1))
...@@ -239,7 +243,7 @@ describe Project do ...@@ -239,7 +243,7 @@ describe Project do
end end
end end
describe :update_merge_requests do describe :update_merge_requests do
let(:project) { Factory :project } let(:project) { Factory :project }
before do before do
...@@ -259,7 +263,7 @@ describe Project do ...@@ -259,7 +263,7 @@ describe Project do
@merge_request.closed.should be_true @merge_request.closed.should be_true
end end
it "should update merge request commits with new one if pushed to source branch" do it "should update merge request commits with new one if pushed to source branch" do
@merge_request.last_commit.should == nil @merge_request.last_commit.should == nil
project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/master", @key.user) project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/master", @key.user)
@merge_request.reload @merge_request.reload
......
...@@ -31,36 +31,46 @@ describe User do ...@@ -31,36 +31,46 @@ describe User do
it { should respond_to(:private_token) } it { should respond_to(:private_token) }
end end
it "should return valid identifier" do describe '#identifier' do
user = User.new(email: "test@mail.com") it "should return valid identifier" do
user.identifier.should == "test_mail_com" user = build(:user, email: "test@mail.com")
end user.identifier.should == "test_mail_com"
end
it "should return identifier without + sign" do it "should return identifier without + sign" do
user = User.new(email: "test+foo@mail.com") user = build(:user, email: "test+foo@mail.com")
user.identifier.should == "test_foo_mail_com" user.identifier.should == "test_foo_mail_com"
end end
it "should execute callback when force_random_password specified" do it "should conform to Gitolite's required identifier pattern" do
user = User.new(email: "test@mail.com", force_random_password: true) user = build(:user, email: "_test@example.com")
user.should_receive(:generate_password) user.identifier.should == 'test_example_com'
user.save end
end end
it "should not generate password by default" do describe '#generate_password' do
user = Factory(:user, password: 'abcdefg', password_confirmation: 'abcdefg') it "should execute callback when force_random_password specified" do
user.password.should == 'abcdefg' user = build(:user, force_random_password: true)
end user.should_receive(:generate_password)
user.save
end
it "should not generate password by default" do
user = create(:user, password: 'abcdefg')
user.password.should == 'abcdefg'
end
it "should generate password when forcing random password" do it "should generate password when forcing random password" do
Devise.stub(:friendly_token).and_return('123456789') Devise.stub(:friendly_token).and_return('123456789')
user = User.create(email: "test1@mail.com", force_random_password: true) user = create(:user, password: 'abcdefg', force_random_password: true)
user.password.should == user.password_confirmation user.password.should == '12345678'
user.password.should == '12345678' end
end end
it "should have authentication token" do describe 'authentication token' do
user = Factory(:user) it "should have authentication token" do
user.authentication_token.should_not == "" user = Factory(:user)
user.authentication_token.should_not be_blank
end
end end
end end
...@@ -10,7 +10,7 @@ describe UsersProject do ...@@ -10,7 +10,7 @@ describe UsersProject do
let!(:users_project) { create(:users_project) } let!(:users_project) { create(:users_project) }
it { should validate_presence_of(:user_id) } it { should validate_presence_of(:user_id) }
it { should validate_uniqueness_of(:user_id).scoped_to(:project_id) } it { should validate_uniqueness_of(:user_id).scoped_to(:project_id).with_message(/already exists/) }
it { should validate_presence_of(:project_id) } it { should validate_presence_of(:project_id) }
end end
......
...@@ -27,38 +27,40 @@ describe Gitlab::API do ...@@ -27,38 +27,40 @@ describe Gitlab::API do
describe "POST /projects" do describe "POST /projects" do
it "should create new project without code and path" do it "should create new project without code and path" do
lambda { expect { post api("/projects", user), name: 'foo' }.to change {Project.count}.by(1)
name = "foo" end
post api("/projects", user), {
name: name it "should not create new project without name" do
} expect { post api("/projects", user) }.to_not change {Project.count}
response.status.should == 201 end
json_response["name"].should == name
json_response["code"].should == name it "should respond with 201 on success" do
json_response["path"].should == name post api("/projects", user), name: 'foo'
}.should change{Project.count}.by(1) response.status.should == 201
end end
it "should create new project" do
lambda { it "should repsond with 404 on failure" do
name = "foo" post api("/projects", user)
path = "bar" response.status.should == 404
code = "bazz" end
post api("/projects", user), {
code: code, it "should assign attributes to project" do
path: path, project = Factory.attributes(:project, {
name: name path: 'path',
} code: 'code',
response.status.should == 201 description: Faker::Lorem.sentence,
json_response["name"].should == name default_branch: 'stable',
json_response["path"].should == path issues_enabled: false,
json_response["code"].should == code wall_enabled: false,
}.should change{Project.count}.by(1) merge_requests_enabled: false,
end wiki_enabled: false
it "should not create project without name" do })
lambda {
post api("/projects", user) post api("/projects", user), project
response.status.should == 404
}.should_not change{Project.count} project.each_pair do |k,v|
json_response[k.to_s].should == v
end
end end
end end
......
require 'spec_helper'
describe Project, "Repository" do
let(:project) { build(:project) }
describe "#empty_repo?" do
it "should return true if the repo doesn't exist" do
project.stub(repo_exists?: false, has_commits?: true)
project.should be_empty_repo
end
it "should return true if the repo has commits" do
project.stub(repo_exists?: true, has_commits?: false)
project.should be_empty_repo
end
it "should return false if the repo exists and has commits" do
project.stub(repo_exists?: true, has_commits?: true)
project.should_not be_empty_repo
end
end
describe "#discover_default_branch" do
let(:master) { double(name: 'master') }
let(:stable) { double(name: 'stable') }
it "returns 'master' when master exists" do
project.should_receive(:heads).and_return([stable, master])
project.discover_default_branch.should == 'master'
end
it "returns non-master when master exists but default branch is set to something else" do
project.default_branch = 'stable'
project.should_receive(:heads).and_return([stable, master])
project.discover_default_branch.should == 'stable'
end
it "returns a non-master branch when only one exists" do
project.should_receive(:heads).and_return([stable])
project.discover_default_branch.should == 'stable'
end
it "returns nil when no branch exists" do
project.should_receive(:heads).and_return([])
project.discover_default_branch.should be_nil
end
end
describe "#root_ref" do
it "returns default_branch when set" do
project.default_branch = 'stable'
project.root_ref.should == 'stable'
end
it "returns 'master' when default_branch is nil" do
project.default_branch = nil
project.root_ref.should == 'master'
end
end
describe "#root_ref?" do
it "returns true when branch is root_ref" do
project.default_branch = 'stable'
project.root_ref?('stable').should be_true
end
it "returns false when branch is not root_ref" do
project.default_branch = nil
project.root_ref?('stable').should be_false
end
end
end
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