diff --git a/.travis.yml b/.travis.yml
index 6a713ea1..ad00ded0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
env:
+ - DB=postgresql
- DB=mysql
before_install:
- sudo apt-get install libicu-dev -y
@@ -18,8 +19,7 @@ services:
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
- - "bundle exec rake db:create RAILS_ENV=test"
- - "bundle exec rake db:migrate RAILS_ENV=test"
+ - "bundle exec rake db:setup RAILS_ENV=test"
- "bundle exec rake db:seed_fu RAILS_ENV=test"
- "sh -e /etc/init.d/xvfb start"
script: "bundle exec rake travis --trace"
diff --git a/CHANGELOG b/CHANGELOG
index 73933e0b..95ed6e8d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,15 @@
v 4.0.0
+ - Reorganized settings
+ - Fixed commits compare
+ - Refactored scss
+ - Improve status checks
+ - Validates presence of User#name
+ - Fixed postgres support
+ - Removed sqlite support
+ - Modified post-receive hook
+ - Milestones can be closed now
+ - Show comment events on dashboard
+ - Quick add team members via group#people page
- [API] expose created date for hooks and SSH keys
- [API] list, create issue notes
- [API] list, create snippet notes
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5f831446..00304dd3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,26 +1,26 @@
-## Contribute to GitLab
+# Contact & support
-If you want to contribute to GitLab, follow this process:
+If you want quick help, head over to our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq).
+Otherwise you can follow our [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) for a more systematic and thorough guide to solving your issues.
-1. Fork the project
-2. Create a feature branch
-3. Code
-4. Create a pull request
-We will only accept pull requests if:
-* Your code has proper tests and all tests pass
-* Your code can be merged w/o problems
-* It won't break existing functionality
-* It's quality code
-* We like it :)
+# Contribute to GitLab
-For examples of feedback on pull requests please look at the [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
+## Recipes
-## Installation
+We collect user submitted installation scripts and config file templates for platforms we don't support officially.
+We believe there is merit in allowing a certain amount of diversity.
+You can get and submit your solution to running/configuring GitLab with your favorite OS/distro, database, web server, cloud hoster, configuration management tool, etc.
-Install the Gitlab development in a virtual machine with the [Gitlab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm). Installing it in a virtual machine makes it much easier to set up all the dependencies for integration testing.
+Help us improve the collection of [GitLab Recipes](https://github.com/gitlabhq/gitlab-recipes/)
-## Running tests
-For more information on running the tests please read the [development tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md)
+## Feature suggestions
+
+Follow the [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) and support other peoples ideas or propose your own.
+
+
+## Code
+
+Follow our [Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide) to set you up for hacking on GitLab.
diff --git a/Gemfile b/Gemfile
index 25a3fa07..49fbcad0 100644
--- a/Gemfile
+++ b/Gemfile
@@ -32,7 +32,7 @@ gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref:
gem "gitolite", '1.1.0'
# Syntax highlighter
-gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", ref: '4db80c599067e2d5f23c5c243bf85b8ca0368ad4'
+gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master"
# Language detection
gem "github-linguist", "~> 2.3.4" , require: "linguist"
@@ -100,7 +100,7 @@ group :assets do
gem "therubyracer"
gem 'chosen-rails', "0.9.8"
- gem 'jquery-atwho-rails', "0.1.6"
+ gem 'jquery-atwho-rails', "0.1.7"
gem "jquery-rails", "2.1.3"
gem "jquery-ui-rails", "2.0.2"
gem "modernizr", "2.6.2"
@@ -124,7 +124,7 @@ group :development, :test do
gem "capybara"
gem "pry"
gem "awesome_print"
- gem "database_cleaner"
+ gem "database_cleaner", ref: "f89c34300e114be99532f14c115b2799a3380ac6", git: "https://github.com/bmabey/database_cleaner.git"
gem "launchy"
gem 'factory_girl_rails'
diff --git a/Gemfile.lock b/Gemfile.lock
index 8242c3c7..d8be14ba 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,10 @@
+GIT
+ remote: https://github.com/bmabey/database_cleaner.git
+ revision: f89c34300e114be99532f14c115b2799a3380ac6
+ ref: f89c34300e114be99532f14c115b2799a3380ac6
+ specs:
+ database_cleaner (0.9.1)
+
GIT
remote: https://github.com/ctran/annotate_models.git
revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
@@ -45,8 +52,8 @@ GIT
GIT
remote: https://github.com/gitlabhq/pygments.rb.git
- revision: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4
- ref: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4
+ revision: db1da0343adf86b49bdc3add04d02d2e80438d38
+ branch: master
specs:
pygments.rb (0.3.2)
posix-spawn (~> 0.3.6)
@@ -140,7 +147,6 @@ GEM
colorize (0.5.8)
crack (0.3.1)
daemons (1.1.9)
- database_cleaner (0.9.1)
devise (2.1.2)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
@@ -227,7 +233,7 @@ GEM
httpauth (0.2.0)
i18n (0.6.1)
journey (1.0.4)
- jquery-atwho-rails (0.1.6)
+ jquery-atwho-rails (0.1.7)
jquery-rails (2.1.3)
railties (>= 3.1.0, < 5.0)
thor (~> 0.14)
@@ -458,7 +464,7 @@ DEPENDENCIES
chosen-rails (= 0.9.8)
coffee-rails (~> 3.2.2)
colored
- database_cleaner
+ database_cleaner!
devise (~> 2.1.0)
draper (~> 0.18.0)
email_spec
@@ -481,7 +487,7 @@ DEPENDENCIES
guard-spinach
haml-rails (~> 0.3.5)
httparty
- jquery-atwho-rails (= 0.1.6)
+ jquery-atwho-rails (= 0.1.7)
jquery-rails (= 2.1.3)
jquery-ui-rails (= 2.0.2)
kaminari (~> 0.14.1)
diff --git a/README.md b/README.md
index 1816629d..629fcefc 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://secure.travis-ci.org/gitlabhq/gitlabhq) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://secure.travis-ci.org/gitlabhq/grit) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
+# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://travis-ci.org/gitlabhq/grit) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
GitLab is a free project and repository management application
diff --git a/VERSION b/VERSION
index 0c042a83..3cdeb6b8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.0.0pre
+4.0.0rc1
diff --git a/app/assets/fonts/korolev-medium-compressed.otf b/app/assets/fonts/korolev-medium-compressed.otf
index e3817cec..a9cd3cbf 100644
Binary files a/app/assets/fonts/korolev-medium-compressed.otf and b/app/assets/fonts/korolev-medium-compressed.otf differ
diff --git a/app/assets/images/ajax_loader_gray.gif b/app/assets/images/ajax_loader_gray.gif
new file mode 100644
index 00000000..af3f618b
Binary files /dev/null and b/app/assets/images/ajax_loader_gray.gif differ
diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png
deleted file mode 100644
index 2d08c9f6..00000000
Binary files a/app/assets/images/logo.png and /dev/null differ
diff --git a/app/assets/images/logo_basic.png b/app/assets/images/logo_basic.png
deleted file mode 100644
index bc5ec128..00000000
Binary files a/app/assets/images/logo_basic.png and /dev/null differ
diff --git a/app/assets/images/logo_text.png b/app/assets/images/logo_text.png
deleted file mode 100644
index c7466393..00000000
Binary files a/app/assets/images/logo_text.png and /dev/null differ
diff --git a/app/assets/images/logo_text_tr.png b/app/assets/images/logo_text_tr.png
deleted file mode 100644
index fdb32ee2..00000000
Binary files a/app/assets/images/logo_text_tr.png and /dev/null differ
diff --git a/app/assets/images/logo_white.png b/app/assets/images/logo_white.png
index 366e3f3f..e3415816 100644
Binary files a/app/assets/images/logo_white.png and b/app/assets/images/logo_white.png differ
diff --git a/app/assets/images/service-disabled-gitlab-ci.png b/app/assets/images/service-disabled-gitlab-ci.png
deleted file mode 100644
index 8d1f9d0b..00000000
Binary files a/app/assets/images/service-disabled-gitlab-ci.png and /dev/null differ
diff --git a/app/assets/images/service-gitlab-ci.png b/app/assets/images/service-gitlab-ci.png
deleted file mode 100644
index bcb30a3f..00000000
Binary files a/app/assets/images/service-gitlab-ci.png and /dev/null differ
diff --git a/app/assets/images/switch_icon.png b/app/assets/images/switch_icon.png
new file mode 100644
index 00000000..7c11f206
Binary files /dev/null and b/app/assets/images/switch_icon.png differ
diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee
index ffc4c409..1cc9d34d 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.coffee
+++ b/app/assets/javascripts/gfm_auto_complete.js.coffee
@@ -1,52 +1,38 @@
# Creates the variables for setting up GFM auto-completion
window.GitLab ?= {}
-GitLab.GfmAutoComplete ?= {}
-
-# Emoji
-data = []
-template = "
${name} "
-GitLab.GfmAutoComplete.Emoji = {data, template}
-
-# Team Members
-data = []
-url = '';
-params = {private_token: '', page: 1}
-GitLab.GfmAutoComplete.Members = {data, url, params}
-
-# Add GFM auto-completion to all input fields, that accept GFM input.
-GitLab.GfmAutoComplete.setup = ->
- input = $('.js-gfm-input')
-
+GitLab.GfmAutoComplete =
# Emoji
- input.atWho ':',
- data: GitLab.GfmAutoComplete.Emoji.data,
- tpl: GitLab.GfmAutoComplete.Emoji.template
+ Emoji:
+ data: []
+ template: '${name} '
# Team Members
- input.atWho '@', (query, callback) ->
- (getMoreMembers = ->
- $.getJSON(GitLab.GfmAutoComplete.Members.url, GitLab.GfmAutoComplete.Members.params)
- .success (members) ->
- # pick the data we need
- newMembersData = $.map(members, (m) -> m.name )
+ Members:
+ data: []
+ url: ''
+ params:
+ private_token: ''
+ template: '${username} ${name}'
- # add the new page of data to the rest
- $.merge(GitLab.GfmAutoComplete.Members.data, newMembersData)
+ # Add GFM auto-completion to all input fields, that accept GFM input.
+ setup: ->
+ input = $('.js-gfm-input')
- # show the pop-up with a copy of the current data
- callback(GitLab.GfmAutoComplete.Members.data[..])
+ # Emoji
+ input.atWho ':',
+ data: @Emoji.data
+ tpl: @Emoji.template
- # are we past the last page?
- if newMembersData.length is 0
- # set static data and stop callbacks
- input.atWho '@',
- data: GitLab.GfmAutoComplete.Members.data
- callback: null
- else
- # get next page
- getMoreMembers()
+ # Team Members
+ input.atWho '@',
+ tpl: @Members.template
+ callback: (query, callback) =>
+ request_params = $.extend({}, @Members.params, query: query)
+ $.getJSON(@Members.url, request_params).done (members) =>
+ new_members_data = $.map(members, (m) ->
+ username: m.username,
+ name: m.name
+ )
+ callback(new_members_data)
- # so the next request gets the next page
- GitLab.GfmAutoComplete.Members.params.page += 1
- ).call()
diff --git a/app/assets/javascripts/issues.js b/app/assets/javascripts/issues.js
index e2fe1075..719d2c17 100644
--- a/app/assets/javascripts/issues.js
+++ b/app/assets/javascripts/issues.js
@@ -1,43 +1,3 @@
-function switchToNewIssue(){
- $(".issues_content").hide("fade", { direction: "left" }, 150, function(){
- $('select#issue_assignee_id').chosen();
- $('select#issue_milestone_id').chosen();
- $("#new_issue_dialog").show("fade", { direction: "right" }, 150);
- $('.top-tabs .add_new').hide();
- disableButtonIfEmptyField("#issue_title", ".save-btn");
- GitLab.GfmAutoComplete.setup();
- });
-}
-
-function switchToEditIssue(){
- $(".issues_content").hide("fade", { direction: "left" }, 150, function(){
- $('select#issue_assignee_id').chosen();
- $('select#issue_milestone_id').chosen();
- $("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
- $('.add_new').hide();
- disableButtonIfEmptyField("#issue_title", ".save-btn");
- GitLab.GfmAutoComplete.setup();
- });
-}
-
-function switchFromNewIssue(){
- backToIssues();
-}
-
-function switchFromEditIssue(){
- backToIssues();
-}
-
-function backToIssues(){
- $("#edit_issue_dialog, #new_issue_dialog").hide("fade", { direction: "right" }, 150, function(){
- $(".issues_content").show("fade", { direction: "left" }, 150, function() {
- $("#edit_issue_dialog").html("");
- $("#new_issue_dialog").html("");
- $('.add_new').show();
- });
- });
-}
-
function initIssuesSearch() {
var href = $('#issue_search_form').attr('action');
var last_terms = '';
@@ -76,23 +36,15 @@ function issuesPage(){
$(this).closest("form").submit();
});
- $("#new_issue_link").click(function(){
- updateNewIssueURL();
- });
-
- $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){
+ $('body').on('ajax:success', '.close_issue, .reopen_issue', function(){
var t = $(this),
totalIssues,
- reopen = t.hasClass('reopen_issue'),
- newIssue = false;
- if( this.id == 'new_issue' ){
- newIssue = true;
- }
- $('.issue_counter, #new_issue').each(function(){
+ reopen = t.hasClass('reopen_issue');
+ $('.issue_counter').each(function(){
var issue = $(this);
totalIssues = parseInt( $(this).html(), 10 );
- if( newIssue || ( reopen && issue.closest('.main_menu').length ) ){
+ if( reopen && issue.closest('.main_menu').length ){
$(this).html( totalIssues+1 );
}else {
$(this).html( totalIssues-1 );
@@ -126,20 +78,3 @@ function issuesCheckChanged() {
$('.issues_filters').show();
}
}
-
-function updateNewIssueURL(){
- var new_issue_link = $("#new_issue_link");
- var milestone_id = $("#milestone_id").val();
- var assignee_id = $("#assignee_id").val();
- var new_href = "";
- if(milestone_id){
- new_href = "issue[milestone_id]=" + milestone_id + "&";
- }
- if(assignee_id){
- new_href = new_href + "issue[assignee_id]=" + assignee_id;
- }
- if(new_href.length){
- new_href = new_issue_link.attr("href") + "?" + new_href;
- new_issue_link.attr("href", new_href);
- }
-};
diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/main.js.coffee
index bdb83f49..f6c398c0 100644
--- a/app/assets/javascripts/main.js.coffee
+++ b/app/assets/javascripts/main.js.coffee
@@ -7,6 +7,18 @@ window.slugify = (text) ->
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
+window.errorMessage = (message) ->
+ ehtml = $("")
+ ehtml.addClass("error_message")
+ ehtml.html(message)
+ ehtml
+
+window.split = (val) ->
+ return val.split( /,\s*/ )
+
+window.extractLast = (term) ->
+ return split( term ).pop()
+
# Disable button if text field is empty
window.disableButtonIfEmptyField = (field_selector, button_selector) ->
field = $(field_selector)
diff --git a/app/assets/javascripts/merge_requests.js b/app/assets/javascripts/merge_requests.js
index 170a0479..ee714f9c 100644
--- a/app/assets/javascripts/merge_requests.js
+++ b/app/assets/javascripts/merge_requests.js
@@ -26,6 +26,12 @@ var MergeRequest = {
self.showState(data.state);
}, "json");
}
+
+ if(self.opts.ci_enable){
+ $.get(self.opts.url_to_ci_check, function(data){
+ self.showCiState(data.status);
+ }, "json");
+ }
},
initTabs:
@@ -79,6 +85,11 @@ var MergeRequest = {
$(".automerge_widget." + state).show();
},
+ showCiState:
+ function(state){
+ $(".ci_widget").hide();
+ $(".ci_widget.ci-" + state).show();
+ },
loadDiff:
function() {
diff --git a/app/assets/javascripts/projects.js.coffee b/app/assets/javascripts/projects.js.coffee
index 1808f057..d03a487c 100644
--- a/app/assets/javascripts/projects.js.coffee
+++ b/app/assets/javascripts/projects.js.coffee
@@ -18,10 +18,3 @@ $ ->
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
-
-class @GraphNav
- @init: ->
- $('.graph svg').css 'position', 'relative'
- $('body').bind 'keyup', (e) ->
- $('.graph svg').animate(left: '+=400') if e.keyCode is 37 # left
- $('.graph svg').animate(left: '-=400') if e.keyCode is 39 # right
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
deleted file mode 100644
index a23d4532..00000000
--- a/app/assets/stylesheets/application.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * This is a manifest file that'll automatically include all the stylesheets available in this directory
- * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
- * the top of the compiled file, but it's generally better to create a new file per style scope.
- *= require jquery.ui.gitlab
- *= require jquery.atwho
- *= require chosen
- *= require_self
- *= require main
-*/
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
new file mode 100644
index 00000000..f93246c1
--- /dev/null
+++ b/app/assets/stylesheets/application.scss
@@ -0,0 +1,52 @@
+/*
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
+ *= require jquery.ui.gitlab
+ *= require jquery.atwho
+ *= require chosen
+ *= require_self
+*/
+
+/**
+ * GitLab bootstrap:
+ */
+@import "gitlab_bootstrap.scss";
+
+@import "common.scss";
+@import "ref_select.scss";
+
+@import "sections/header.scss";
+@import "sections/nav.scss";
+@import "sections/commits.scss";
+@import "sections/issues.scss";
+@import "sections/projects.scss";
+@import "sections/snippets.scss";
+@import "sections/votes.scss";
+@import "sections/merge_requests.scss";
+@import "sections/graph.scss";
+@import "sections/events.scss";
+@import "sections/themes.scss";
+@import "sections/tree.scss";
+@import "sections/notes.scss";
+@import "sections/profile.scss";
+@import "sections/login.scss";
+@import "sections/editor.scss";
+
+@import "highlight/white.scss";
+@import "highlight/dark.scss";
+
+/**
+ * UI themes:
+ */
+@import "themes/ui_basic.scss";
+@import "themes/ui_mars.scss";
+@import "themes/ui_modern.scss";
+@import "themes/ui_gray.scss";
+@import "themes/ui_color.scss";
+
+/**
+ * Styles for JS behaviors.
+ */
+@import "behaviors.scss";
+
diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss
index 44bfb619..6d4c8151 100644
--- a/app/assets/stylesheets/common.scss
+++ b/app/assets/stylesheets/common.scss
@@ -13,20 +13,12 @@ body {
margin: 0 0;
}
-.container .sidebar {
- width: 200px;
- height: 100%;
- min-height: 450px;
- float: right;
-}
-
-
.visible_link,
.author_link {
color: $link_color;
}
-.help li { color:#111 }
+.help li { color:$style_color; }
.back_link {
text-decoration: underline;
@@ -65,6 +57,9 @@ table a code {
background: url(ajax_loader.gif) no-repeat center center;
width: 40px;
height: 40px;
+ &.loading-gray {
+ background: url(ajax_loader_gray.gif) no-repeat center center;
+ }
}
/** FLASH message **/
@@ -96,28 +91,17 @@ table a code {
margin-right:50px
}
-.handle:hover {
- cursor: move;
-}
-
span.update-author {
display: block;
-}
-span.update-author {
color: #999;
font-weight: normal;
font-style: italic;
-}
-span.update-author strong {
- font-weight: bold;
- font-style: normal;
+ strong {
+ font-weight: bold;
+ font-style: normal;
+ }
}
-/** UPDATE ITEM **/
-span.update-author {
- display: block;
-}
-/** END UPDATE ITEM **/
.dashboard-loader {
float: left;
margin: 10px;
@@ -264,21 +248,6 @@ input.git_clone_url {
}
-/** bordered list **/
-ul.bordered-list {
- margin: 5px 0px;
- padding: 0px;
- li {
- padding: 5px 0;
- border-bottom: 1px solid #EEE;
- overflow: hidden;
- display: block;
- margin: 0px;
- }
-}
-
-ul.bordered-list li:last-child { border:none }
-
.line_holder {
&:hover {
td {
@@ -316,98 +285,6 @@ p.time {
}
-.ico {
- background: url("images.png") no-repeat -85px -77px;
- width: 19px;
- height: 16px;
- float: left;
- position: relative;
- margin-right: 10px;
- top: 8px;
-
- &.project {
- background-position: -37px -77px;
- }
-
- &.activities {
- background-position:-162px -22px;
- }
- &.projects {
- background-position:-209px -21px;
- }
-}
-
-.leftbar {
- h5, .title {
- padding: 5px 10px;
- }
-
- h4 {
- font-size: 14px;
- padding: 2px 10px;
- color: #666;
- border-bottom: 1px solid #f1f1f1;
- }
- a:last-child h4 { border: none; }
-
- a:hover {
- h4 {
- color: #111;
- background: $hover;
- border-color: #CCC;
- .ico.project {
- background-position:-209px -21px;
- }
- }
- }
- .bottom {
- padding: 10px;
- }
-}
-
-.votes {
- font-size: 13px;
- line-height: 15px;
- .progress {
- height: 4px;
- margin: 0;
- .bar {
- float: left;
- height: 100%;
- }
- .bar-success {
- @include linear-gradient(#62C462, #51A351);
- background-color: #468847;
- }
- .bar-danger {
- @include linear-gradient(#EE5F5B, #BD362F);
- background-color: #B94A48;
- }
- }
- .upvotes {
- display: inline-block;
- color: #468847;
- }
- .downvotes {
- display: inline-block;
- color: #B94A48;
- }
-}
-.votes-block {
- margin: 14px 6px 6px 0;
- .downvotes {
- float: right;
- }
-}
-.votes-inline {
- display: inline-block;
- margin: 0 8px;
- .progress {
- display: inline-block;
- padding: 0 0 2px;
- width: 45px;
- }
-}
/* Fix for readme code (stopped it from being yellow) */
.readme {
@@ -420,7 +297,6 @@ p.time {
}
}
-
.highlight_word {
background: #EEDC94;
}
@@ -428,23 +304,16 @@ p.time {
.status_info {
font-size: 14px;
padding: 5px 15px;
- line-height: 24px;
- width: 60px;
+ line-height: 26px;
text-align: center;
- float: left;
- margin-right: 20px;
+ float: right;
+ position: relative;
+ top: -5px;
+ @include border-radius(4px);
- &.success {
- background: #5BB75B;
- color: white;
- text-shadow: 0 1px #111;
- border-color: #9A9;
- }
&.error {
background: #DA4E49;
- border-color: #BD362F;
- color: white;
- text-shadow: 0 1px #111;
+ color: #FFF;
}
}
@@ -463,16 +332,6 @@ p.time {
height: 150px;
}
-.gitlab_pagination {
- span a { color: $link_color; }
- .prev, .next, .current, .page a {
- padding: 10px;
- }
- .current {
- border-bottom: 2px solid $style_color;
- }
-}
-
// Fixes alignment on notes.
.new_note {
label {
@@ -647,9 +506,14 @@ pre {
}
}
-.milestone .progress {
- margin-bottom: 0;
- margin-top: 4px;
+.milestone {
+ &.milestone-closed {
+ background: #eee;
+ }
+ .progress {
+ margin-bottom: 0;
+ margin-top: 4px;
+ }
}
.float-link {
diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss
new file mode 100644
index 00000000..f53e0e50
--- /dev/null
+++ b/app/assets/stylesheets/gitlab_bootstrap.scss
@@ -0,0 +1,26 @@
+/** Override bootstrap variables **/
+$baseFontSize: 13px !default;
+$baseLineHeight: 18px !default;
+
+// BOOTSTRAP
+@import "bootstrap";
+@import "bootstrap/responsive-utilities";
+@import "bootstrap/responsive-1200px-min";
+
+@import "font-awesome";
+
+/**
+ * GitLab bootstrap.
+ * Overrides some styles of twitter bootstrap.
+ * Also give some common classes for GitLab app
+ */
+@import "gitlab_bootstrap/variables.scss";
+@import "gitlab_bootstrap/fonts.scss";
+@import "gitlab_bootstrap/mixins.scss";
+@import "gitlab_bootstrap/common.scss";
+@import "gitlab_bootstrap/typography.scss";
+@import "gitlab_bootstrap/buttons.scss";
+@import "gitlab_bootstrap/blocks.scss";
+@import "gitlab_bootstrap/files.scss";
+@import "gitlab_bootstrap/tables.scss";
+@import "gitlab_bootstrap/lists.scss";
diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss
index ecd6cf7e..f9c8b7b0 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss
@@ -31,6 +31,7 @@
.middle_box_content,
.bottom_box_content {
padding: 15px;
+ word-wrap: break-word;
pre {
background: none !important;
@@ -40,6 +41,15 @@
}
}
+ .top_box_content {
+ .box-title {
+ color: $style_color;
+ font-size: 18px;
+ font-weight: normal;
+ line-height: 28px;
+ }
+ }
+
.middle_box_content {
@include border-radius(0);
border: none;
@@ -64,7 +74,7 @@
border: 1px solid #eaeaea;
@include border-radius(4px);
-
+
border-color: #CCC;
@include solid-shade;
@@ -83,6 +93,10 @@
border-top: 1px solid #eaeaea;
border-bottom: 1px solid #bbb;
+ > a {
+ text-shadow: 0 1px 1px #fff;
+ }
+
&.small {
line-height: 28px;
font-size: 14px;
@@ -138,19 +152,6 @@
}
}
- li, .wll {
- padding: 10px;
- &:first-child {
- @include border-radius(4px 4px 0 0);
- border-top: none;
- }
-
- &:last-child {
- @include border-radius(0 0 4px 4px);
- border: none;
- }
- }
-
.ui-box-body {
padding: 10px;
}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/common.scss b/app/assets/stylesheets/gitlab_bootstrap/common.scss
index 9a4f2e80..3bb7cdbf 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/common.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/common.scss
@@ -10,11 +10,6 @@
/** COMMON CLASSES **/
.left { float:left }
.right { float:right!important }
-.width-50p { width:50% }
-.width-49p { width:49% }
-.width-30p { width:30% }
-.width-65p { width:65% }
-.width-100p { width:100% }
.append-bottom-10 { margin-bottom:10px }
.append-bottom-20 { margin-bottom:20px }
.prepend-top-10 { margin-top:10px }
@@ -30,6 +25,7 @@
.borders { border: 1px solid #ccc; @include shade; }
.hint { font-style: italic; color: #999; }
.light { color: #888 }
+.tiny { font-weight: normal }
/** PILLS & TABS**/
.nav-pills a:hover { background-color: #888; }
@@ -99,18 +95,21 @@ input[type='search'].search-text-input {
border: 1px solid #ccc;
}
+input[type='text'].danger {
+ background: #F2DEDE!important;
+ border-color: #D66;
+ text-shadow: 0 1px 1px #fff
+}
+
fieldset legend { font-size: 17px; }
-ul.nav.nav-projects-tabs {
- @extend .nav-tabs;
-
- padding-left: 8px;
-
- li {
- a {
- padding: 4px 20px;
- margin-top: 2px;
- border-color: #DDD;
- }
+/** PAGINATION **/
+.gitlab_pagination {
+ span a { color: $link_color; }
+ .prev, .next, .current, .page a {
+ padding: 10px;
+ }
+ .current {
+ border-bottom: 2px solid $style_color;
}
}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/files.scss b/app/assets/stylesheets/gitlab_bootstrap/files.scss
index e4924a49..83954da5 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/files.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/files.scss
@@ -43,11 +43,15 @@
padding: 0 4px;
}
padding: 20px;
- h1, h2 {
- line-height: 46px;
- }
- h3, h4 {
- line-height: 40px;
+
+ h1 { font-size: 26px; line-height: 46px; }
+ h2 { font-size: 22px; line-height: 42px; }
+ h3 { font-size: 20px; line-height: 40px; }
+ h4 { font-size: 18px; line-height: 32px; }
+ h5 { font-size: 16px; line-height: 26px; }
+
+ .white .highlight pre {
+ background: #f5f5f5;
}
}
diff --git a/app/assets/stylesheets/fonts.scss b/app/assets/stylesheets/gitlab_bootstrap/fonts.scss
similarity index 100%
rename from app/assets/stylesheets/fonts.scss
rename to app/assets/stylesheets/gitlab_bootstrap/fonts.scss
diff --git a/app/assets/stylesheets/gitlab_bootstrap/lists.scss b/app/assets/stylesheets/gitlab_bootstrap/lists.scss
index 5bd087b0..edaf3cef 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/lists.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/lists.scss
@@ -1,23 +1,38 @@
-/** LISTS **/
-
-ul {
- /**
- * List li block element #1
- *
- */
- .wll {
+/**
+ * Well styled list
+ *
+ */
+.well-list {
+ margin: 0;
+ list-style: none;
+ li {
background-color: #FFF;
- padding: 10px 5px;
+ padding: 10px;
min-height: 20px;
border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+ &.disabled {
+ color: #888;
+ }
+
&.smoke { background-color: #f5f5f5; }
+
&:hover {
background: $hover;
border-bottom: 1px solid #ADF;
}
- &:last-child { border:none }
+
+ &:first-child {
+ @include border-radius(4px 4px 0 0);
+ border-top: none;
+ }
+
+ &:last-child {
+ @include border-radius(0 0 4px 4px);
+ border: none;
+ }
+
.author { color: #999; }
p {
@@ -29,6 +44,11 @@ ul {
top: 3px;
}
}
+
+ .well-title {
+ font-size: 14px;
+ line-height: 18px;
+ }
}
}
@@ -39,3 +59,17 @@ ol, ul {
}
}
}
+
+/** light list with border-bottom between li **/
+ul.bordered-list {
+ margin: 5px 0px;
+ padding: 0px;
+ li {
+ padding: 5px 0;
+ border-bottom: 1px solid #EEE;
+ overflow: hidden;
+ display: block;
+ margin: 0px;
+ &:last-child { border:none }
+ }
+}
diff --git a/app/assets/stylesheets/mixins.scss b/app/assets/stylesheets/gitlab_bootstrap/mixins.scss
similarity index 90%
rename from app/assets/stylesheets/mixins.scss
rename to app/assets/stylesheets/gitlab_bootstrap/mixins.scss
index 441a85f3..81830368 100644
--- a/app/assets/stylesheets/mixins.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/mixins.scss
@@ -57,4 +57,13 @@
@mixin solid-shade {
@include box-shadow(0 0 0 3px #f1f1f1);
-}
\ No newline at end of file
+}
+
+@mixin header-font {
+ color: $style_color;
+ text-shadow: 0 1px 1px #FFF;
+ font-family: 'Korolev', sans-serif;
+ font-size: 28px;
+ line-height: 48px;
+ font-weight: normal;
+}
diff --git a/app/assets/stylesheets/variables.scss b/app/assets/stylesheets/gitlab_bootstrap/variables.scss
similarity index 83%
rename from app/assets/stylesheets/variables.scss
rename to app/assets/stylesheets/gitlab_bootstrap/variables.scss
index ba78c835..869eb168 100644
--- a/app/assets/stylesheets/variables.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/variables.scss
@@ -2,4 +2,4 @@
$primary_color: #2FA0BB;
$link_color: #3A89A3;
$style_color: #474D57;
-$hover: #D9EDF7;
\ No newline at end of file
+$hover: #D9EDF7;
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index 4196ea7a..6018ff70 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -1,7 +1,8 @@
.black .highlight {
+ background-color: #333;
pre {
- background-color: #333;
color: #eee;
+ background: inherit;
}
.hll { display: block; background-color: darken($hover, 65%) }
diff --git a/app/assets/stylesheets/jquery.ui.gitlab.css b/app/assets/stylesheets/jquery.ui.gitlab.css
index 17185765..5c51600b 100644
--- a/app/assets/stylesheets/jquery.ui.gitlab.css
+++ b/app/assets/stylesheets/jquery.ui.gitlab.css
@@ -1,27 +1,3 @@
-/*
- * jQuery UI CSS Framework 1.8.7
- *
- * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Theming/API
- */
-
-/* Layout helpers
-----------------------------------*/
-.ui-helper-hidden { display: none; }
-.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
-.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
-.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
-.ui-helper-clearfix { display: inline-block; }
-/* required comment for clearfix to work in Opera \*/
-* html .ui-helper-clearfix { height:1%; }
-.ui-helper-clearfix { display:block; }
-/* end clearfix */
-.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
-
-
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
@@ -140,26 +116,6 @@
/* Overlays */
.ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }
-/*
- * jQuery UI Resizable 1.8.7
- *
- * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Resizable#theming
- */
-.ui-resizable { position: relative;}
-.ui-resizable-handle { position: absolute; font-size: 0.1px; z-index: 999; display: block;}
-.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
-.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
-.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
-.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
-.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
-.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
-.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
-.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
-.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}
/*
* jQuery UI Selectable 1.8.7
*
@@ -240,34 +196,7 @@
cursor: pointer;
font-weight: bold;
}
-/*
- * jQuery UI Slider 1.8.7
- *
- * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * http://docs.jquery.com/UI/Slider#theming
- */
-.ui-slider { position: relative; text-align: left; background: #d7d7d7; z-index: 1; }
-.ui-slider { -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; }
-.ui-slider .ui-slider-handle { background: url(slider_handles.png) 0px -23px no-repeat; position: absolute; z-index: 2; width: 23px; height: 23px; cursor: default; border: none; outline: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
-.ui-slider .ui-state-hover, .ui-slider .ui-state-active { background-position: 0 0; }
-.ui-slider .ui-slider-range { background: #a3cae0; position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
-.ui-slider .ui-slider-range { -moz-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; -webkit-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; }
-
-.ui-slider-horizontal { height: 5px; }
-.ui-slider-horizontal .ui-slider-handle { top: -8px; margin-left: -13px; }
-.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
-.ui-slider-horizontal .ui-slider-range-min { left: 0; }
-.ui-slider-horizontal .ui-slider-range-max { right: 0; }
-
-.ui-slider-vertical { width: 5px; height: 100px; }
-.ui-slider-vertical .ui-slider-handle { left: -8px; margin-left: 0; margin-bottom: -13px; }
-.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
-.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
-.ui-slider-vertical .ui-slider-range-max { top: 0; }
/*
* jQuery UI Datepicker 1.8.7
*
@@ -326,45 +255,3 @@
.ui-datepicker table .ui-state-highlight { border-color: #ADE; }
.ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; }
.ui-datepicker-calendar .ui-state-active { background: #D9EDF7; border-color: #ADE; color: #3A89A3; font-weight: bold; text-shadow: 0 1px 1px #fff; }
-
-/* with multiple calendars */
-.ui-datepicker.ui-datepicker-multi { width:auto; }
-.ui-datepicker-multi .ui-datepicker-group { float:left; }
-.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
-.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
-.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
-.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
-.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
-.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
-.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
-.ui-datepicker-row-break { clear:both; width:100%; }
-
-
-/* Extra Input Field Styling */
-.ui-form textarea, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]) {
- padding: 3px;
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
- border-radius: 2px;
- border: 1px solid #cecece;
- outline: none;
- -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2);
- -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2);
- box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2);
- -webkit-transition: all 250ms ease-in-out;
- -moz-transition: all 250ms ease-in-out;
- -o-transition: all 250ms ease-in-out;
- transition: all 250ms ease-in-out;
-}
-.ui-form textarea:hover, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):hover {
- border: 1px solid #bdbdbd;
- -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2);
- -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2);
- box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2);
-}
-.ui-form textarea:focus, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):focus {
- border: 1px solid #95bdd4;
- -webkit-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2);
- -moz-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2);
- box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2);
-}
diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss
deleted file mode 100644
index 98d75521..00000000
--- a/app/assets/stylesheets/main.scss
+++ /dev/null
@@ -1,144 +0,0 @@
-/** Override bootstrap variables **/
-$baseFontSize: 13px !default;
-$baseLineHeight: 18px !default;
-
-// BOOTSTRAP
-@import "bootstrap";
-@import "bootstrap/responsive-utilities";
-@import "bootstrap/responsive-1200px-min";
-
-// FONT AWESOME
-@import "font-awesome";
-
-/**
- * Variables
- * Contains colors
- */
-@import "variables.scss";
-
-/**
- * Custom fonts
- * Contains @font-face font Korolev and default $monotype
- */
-@import "fonts.scss";
-
-/**
- * General mixins.
- * Contains rounded borders, gradients and shades
- */
-@import "mixins.scss";
-
-/**
- * Header of application.
- * Contain application logo, search panel, profile icon
- */
-@import "sections/header.scss";
-
-/**
- * Navigation menu of application.
- * Panel with links to pages depends on project, profile or admin area
- */
-@import "sections/nav.scss";
-
-/**
- * This file represent some UI that can be changed
- * during web app restyle or theme select.
- *
- * Next items should be placed there
- * - link, button colors
- * - header restyles
- * - main menu restyles
- *
- */
-@import "themes/ui_basic.scss";
-
-/**
- * UI themes:
- */
-@import "themes/ui_mars.scss";
-@import "themes/ui_modern.scss";
-@import "themes/ui_gray.scss";
-@import "themes/ui_color.scss";
-
-/**
- * GitLab bootstrap.
- * Overrides some styles of twitter bootstrap.
- * Also give some common classes for GitLab app
- */
-@import "gitlab_bootstrap/common.scss";
-@import "gitlab_bootstrap/typography.scss";
-@import "gitlab_bootstrap/buttons.scss";
-@import "gitlab_bootstrap/blocks.scss";
-@import "gitlab_bootstrap/files.scss";
-@import "gitlab_bootstrap/tables.scss";
-@import "gitlab_bootstrap/lists.scss";
-
-
-/**
- * Most of application styles placed here.
- * This file represent common UI that should not be changed between themes
- * or project restyling like form width or user avatar class or commit title
- *
- * TODO: clean it
- */
-@import "common.scss";
-
-/**
- * Styles necessary to support JS behaviours.
- */
-@import "behaviors.scss";
-
-/**
- * Styles related to specific part of app
- */
-@import "sections/commits.scss";
-@import "sections/issues.scss";
-@import "sections/projects.scss";
-@import "sections/merge_requests.scss";
-@import "sections/graph.scss";
-@import "sections/events.scss";
-@import "sections/themes.scss";
-
-/**
- * This scss file redefine chozen selectbox styles for
- * project Branch/Tag select element
- */
-@import "ref_select.scss";
-
-/**
- * Code (files list) styles. Browsing project files there
- */
-@import "sections/tree.scss";
-
-/**
- * This file represent notes(comments) styles
- */
-@import "sections/notes.scss";
-
-/**
- * This file represent profile styles
- */
-@import "sections/profile.scss";
-
-/**
- * Devise styles
- */
-@import "sections/login.scss";
-
-/**
- * CODE HIGHTLIGHT BASE
- *
- */
-@import "highlight/white.scss";
-
-/**
- * CODE HIGHTLIGHT DARK schema
- *
- */
-@import "highlight/dark.scss";
-
-/**
- * File Editor styles
- *
- */
-@import "sections/editor.scss";
diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss
index b96e460c..1cae7b0c 100644
--- a/app/assets/stylesheets/sections/commits.scss
+++ b/app/assets/stylesheets/sections/commits.scss
@@ -232,8 +232,6 @@
/** COMMIT ROW **/
.commit {
- @extend .wll;
-
.browse_code_link_holder {
@extend .span2;
float: right;
@@ -305,3 +303,17 @@
color: #fff;
font-family: $monospace;
}
+
+
+.commits-compare-switch{
+ background: url("switch_icon.png") no-repeat center center;
+ width: 16px;
+ height: 18px;
+ text-indent: -9999px;
+ float: left;
+ margin-right: 9px;
+ border: 1px solid #DDD;
+ @include border-radius(4px);
+ padding: 4px;
+ background-color: #EEE;
+}
diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss
index 28551d9a..071a9c35 100644
--- a/app/assets/stylesheets/sections/events.scss
+++ b/app/assets/stylesheets/sections/events.scss
@@ -31,7 +31,6 @@
*
*/
.event-item {
- min-height: 40px;
border-bottom: 1px solid #eee;
.event-title {
color: #333;
@@ -50,14 +49,18 @@
}
}
.avatar {
- width: 32px;
+ position: relative;
+ top: -3px;
}
.event_icon {
+ position: relative;
float: right;
border: 1px solid #EEE;
padding: 5px;
@include border-radius(5px);
background: #F9F9F9;
+ margin-left: 10px;
+ top: -6px;
img {
width: 20px;
}
@@ -71,9 +74,8 @@
}
}
- padding: 15px 5px;
+ padding: 16px 5px;
&:last-child { border:none }
- .wll:hover { background:none }
.event_commits {
margin-top: 5px;
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss
index 4171c00a..c1b210be 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/sections/header.scss
@@ -44,14 +44,9 @@ header {
background: url('logo_dark.png') no-repeat 0px 2px;
float: left;
margin-left: 2px;
- font-size: 30px;
- line-height: 48px;
- font-weight: normal;
- color: $style_color;
- text-shadow: 0 1px 1px #FFF;
padding-left: 45px;
height: 40px;
- font-family: 'Korolev', sans-serif;
+ @include header-font;
}
}
}
@@ -66,12 +61,7 @@ header {
float: left;
margin: 0;
margin-right: 30px;
- font-size: 30px;
- line-height: 48px;
- font-weight: normal;
- color: $style_color;
- text-shadow: 0 1px 1px #FFF;
- font-family: 'Korolev', sans-serif;
+ @include header-font;
}
/**
@@ -172,7 +162,7 @@ header {
display: none;
z-index: 100000;
@include border-radius(4px);
- width: 100px;
+ width: 130px;
position: absolute;
right: 5px;
top: 38px;
@@ -181,7 +171,7 @@ header {
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
a {
color: #fff;
- padding: 7px 10px;
+ padding: 12px 15px;
display: block;
text-shadow: none;
border-bottom: 1px solid #666;
@@ -204,8 +194,8 @@ header {
}
&:last-child {
@include border-radius(0 0 5px 5px);
- border-bottom: 0;
- }
+ border-bottom: 0;
+ }
}
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index ef3821f2..fd995728 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -121,12 +121,3 @@ input.check_all_issues {
#update_status {
width: 100px;
}
-
-
-/**
- * Milestones list
- *
- */
-.milestone {
- @extend .wll;
-}
diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss
index a5ec1756..4808117d 100644
--- a/app/assets/stylesheets/sections/merge_requests.scss
+++ b/app/assets/stylesheets/sections/merge_requests.scss
@@ -136,9 +136,3 @@ li.merge_request {
}
}
}
-
-.status-badge {
- height: 32px;
- width: 100%;
- @include border-radius(5px);
-}
diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss
index 595568fc..bc19bc75 100644
--- a/app/assets/stylesheets/sections/nav.scss
+++ b/app/assets/stylesheets/sections/nav.scss
@@ -3,15 +3,13 @@
*
*/
ul.main_menu {
- @include border-radius(4px);
margin: auto;
margin: 30px 0;
- border: 1px solid #BBB;
+ margin-top: 10px;
+ border-bottom: 1px solid #DDD;
height: 37px;
- @include bg-gray-gradient;
position: relative;
overflow: hidden;
- @include shade;
.count {
position: relative;
top: -1px;
@@ -24,9 +22,6 @@ ul.main_menu {
line-height: 14px;
text-align: center;
color: #777;
- background: #f2f2f2;
- border-top: 1px solid #CCC;
- @include border-radius(8px);
}
.label {
background: $hover;
@@ -38,23 +33,10 @@ ul.main_menu {
margin: 0;
display: table-cell;
width: 1%;
- border-right: 1px solid #DDD;
- border-left: 1px solid #EEE;
- border-bottom: 2px solid #CFCFCF;
-
- &:first-child{
- @include border-radius(5px 0 0 5px);
- border-left: 0;
- }
-
&.active {
- background-color: #D5D5D5;
- border-right: 1px solid #BBB;
- border-left: 1px solid #BBB;
- @include border-radius(0 0 1px 1px);
- &:first-child{
- border-bottom: none;
- border-left: none;
+ border-bottom: 2px solid #474D57;
+ a {
+ color: $style_color;
}
}
@@ -73,10 +55,10 @@ ul.main_menu {
a {
display: block;
text-align: center;
- font-weight: bold;
+ font-weight: normal;
height: 35px;
line-height: 36px;
- color: $style_color;
+ color: #777;
text-shadow: 0 1px 1px white;
padding: 0 10px;
}
diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss
index a230f296..717f8502 100644
--- a/app/assets/stylesheets/sections/projects.scss
+++ b/app/assets/stylesheets/sections/projects.scss
@@ -4,12 +4,11 @@
}
.side {
- @extend .span4;
@extend .right;
.groups_box,
.projects_box {
- h5 {
+ > h5 {
color: $style_color;
font-size: 16px;
text-shadow: 0 1px 1px #fff;
@@ -17,37 +16,22 @@
line-height: 32px;
font-size: 14px;
}
- ul {
- li {
- padding: 0;
- a {
- display: block;
- .group_name {
- font-size: 14px;
- line-height: 18px;
- }
- .project_name {
- color: #4fa2bd;
- font-size: 14px;
- line-height: 18px;
- }
- .arrow {
- float: right;
- padding: 10px;
- margin: 0;
- }
- .last_activity {
- padding-top: 5px;
- display: block;
- span, strong {
- font-size: 12px;
- color: #666;
- }
- }
+ .nav-projects-tabs li { padding: 0; }
+ .well-list {
+ .arrow {
+ float: right;
+ padding: 10px;
+ margin: 0;
+ }
+ .last_activity {
+ padding-top: 5px;
+ display: block;
+ span, strong {
+ font-size: 12px;
+ color: #666;
}
}
}
- @extend .leftbar;
@extend .ui-box;
}
}
@@ -117,3 +101,25 @@
}
}
+
+ul.nav.nav-projects-tabs {
+ @extend .nav-tabs;
+
+ padding-left: 8px;
+
+ li {
+ a {
+ padding: 4px 20px;
+ margin-top: 2px;
+ border-color: #DDD;
+ background-color: #EEE;
+ text-shadow: 0 1px 1px white;
+ color: #555;
+ }
+ &.active {
+ a {
+ font-weight: bold;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/sections/snippets.scss b/app/assets/stylesheets/sections/snippets.scss
new file mode 100644
index 00000000..3944814f
--- /dev/null
+++ b/app/assets/stylesheets/sections/snippets.scss
@@ -0,0 +1,9 @@
+.snippet.file_holder {
+ .file_title {
+ .snippet-file-name {
+ position: relative;
+ top: -4px;
+ left: -4px;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/sections/votes.scss
new file mode 100644
index 00000000..4686f542
--- /dev/null
+++ b/app/assets/stylesheets/sections/votes.scss
@@ -0,0 +1,43 @@
+.votes {
+ font-size: 13px;
+ line-height: 15px;
+ .progress {
+ height: 4px;
+ margin: 0;
+ .bar {
+ float: left;
+ height: 100%;
+ }
+ .bar-success {
+ @include linear-gradient(#62C462, #51A351);
+ background-color: #468847;
+ }
+ .bar-danger {
+ @include linear-gradient(#EE5F5B, #BD362F);
+ background-color: #B94A48;
+ }
+ }
+ .upvotes {
+ display: inline-block;
+ color: #468847;
+ }
+ .downvotes {
+ display: inline-block;
+ color: #B94A48;
+ }
+}
+.votes-block {
+ margin: 14px 6px 6px 0;
+ .downvotes {
+ float: right;
+ }
+}
+.votes-inline {
+ display: inline-block;
+ margin: 0 8px;
+ .progress {
+ display: inline-block;
+ padding: 0 0 2px;
+ width: 45px;
+ }
+}
diff --git a/app/assets/stylesheets/themes/ui_basic.scss b/app/assets/stylesheets/themes/ui_basic.scss
index fee17989..b3777277 100644
--- a/app/assets/stylesheets/themes/ui_basic.scss
+++ b/app/assets/stylesheets/themes/ui_basic.scss
@@ -4,18 +4,6 @@
*
*/
.ui_basic {
- /*
- * Common styles
- *
- */
- a {
- color: $link_color;
- &:hover {
- text-decoration: none;
- color: $primary_color;
- }
- }
-
.app_logo {
.separator {
margin-left: 0;
diff --git a/app/contexts/project_update_context.rb b/app/contexts/project_update_context.rb
index e28d43d0..5b77d0a7 100644
--- a/app/contexts/project_update_context.rb
+++ b/app/contexts/project_update_context.rb
@@ -2,7 +2,9 @@ class ProjectUpdateContext < BaseContext
def execute(role = :default)
namespace_id = params[:project].delete(:namespace_id)
- if namespace_id.present?
+ allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin
+
+ if allowed_transfer && namespace_id.present?
if namespace_id == Namespace.global_id
if project.namespace.present?
# Transfer to global namespace from anyone
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 8a0a9e9b..a492e666 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -2,7 +2,7 @@ class Admin::GroupsController < AdminController
before_filter :group, only: [:edit, :show, :update, :destroy, :project_update]
def index
- @groups = Group.scoped
+ @groups = Group.order('name ASC')
@groups = @groups.search(params[:name]) if params[:name].present?
@groups = @groups.page(params[:page]).per(20)
end
@@ -11,6 +11,7 @@ class Admin::GroupsController < AdminController
@projects = Project.scoped
@projects = @projects.not_in_group(@group) if @group.projects.present?
@projects = @projects.all
+ @projects.reject!(&:empty_repo?)
end
def new
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index e61f94f8..4fea8709 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -4,12 +4,13 @@ class Admin::ProjectsController < AdminController
def index
@projects = Project.scoped
@projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present?
+ @projects = @projects.where(namespace_id: nil) if params[:namespace_id] == Namespace.global_id
@projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
end
def show
- @users = User.scoped
+ @users = User.active
@users = @users.not_in_project(@project) if @project.users.present?
@users = @users.all
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 744b1912..5f259bd7 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -3,7 +3,7 @@ class Admin::UsersController < AdminController
@admin_users = User.scoped
@admin_users = @admin_users.filter(params[:filter])
@admin_users = @admin_users.search(params[:name]) if params[:name].present?
- @admin_users = @admin_users.order("updated_at DESC").page(params[:page])
+ @admin_users = @admin_users.order("name ASC").page(params[:page])
end
def show
@@ -30,7 +30,7 @@ class Admin::UsersController < AdminController
def new
- @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin)
+ @admin_user = User.new({ projects_limit: Gitlab.config.gitlab.default_projects_limit }, as: :admin)
end
def edit
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 5735c1d2..75cd8f15 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -112,6 +112,10 @@ class ApplicationController < ActionController::Base
render file: Rails.root.join("public", "404"), layout: false, status: "404"
end
+ def render_403
+ render file: Rails.root.join("public", "403"), layout: false, status: "403"
+ end
+
def require_non_empty_project
redirect_to @project if @project.empty_repo?
end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 461dd51b..1fcadbfe 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -7,6 +7,8 @@ class DashboardController < ApplicationController
def index
@groups = current_user.authorized_groups
+ @has_authorized_projects = @projects.count > 0
+
@projects = case params[:scope]
when 'personal' then
@projects.personal(current_user)
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 93c49536..c82edb4c 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -21,7 +21,7 @@ class GroupsController < ApplicationController
# Get authored or assigned open merge requests
def merge_requests
- @merge_requests = current_user.cared_merge_requests
+ @merge_requests = current_user.cared_merge_requests.opened
@merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20)
end
@@ -49,6 +49,7 @@ class GroupsController < ApplicationController
def people
@project = group.projects.find(params[:project_id]) if params[:project_id]
@users = @project ? @project.users : group.users
+ @users.sort_by!(&:name)
if @project
@team_member = @project.users_projects.new
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index 443763cd..9917d198 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -1,6 +1,6 @@
class IssuesController < ProjectResourceController
before_filter :module_enabled
- before_filter :issue, only: [:edit, :update, :destroy, :show]
+ before_filter :issue, only: [:edit, :update, :show]
# Allow read any issue
before_filter :authorize_read_issue!
@@ -11,9 +11,6 @@ class IssuesController < ProjectResourceController
# Allow modify issue
before_filter :authorize_modify_issue!, only: [:edit, :update]
- # Allow destroy issue
- before_filter :authorize_admin_issue!, only: [:destroy]
-
respond_to :js, :html
def index
@@ -79,15 +76,6 @@ class IssuesController < ProjectResourceController
end
end
- def destroy
- @issue.destroy
-
- respond_to do |format|
- format.html { redirect_to project_issues_path }
- format.js { render nothing: true }
- end
- end
-
def sort
return render_404 unless can?(current_user, :admin_issue, @project)
diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb
index 841e8085..355f4d79 100644
--- a/app/controllers/merge_requests_controller.rb
+++ b/app/controllers/merge_requests_controller.rb
@@ -1,6 +1,6 @@
class MergeRequestsController < ProjectResourceController
before_filter :module_enabled
- before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check]
+ before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status]
before_filter :validates_merge_request, only: [:show, :diffs]
before_filter :define_show_vars, only: [:show, :diffs]
@@ -13,9 +13,6 @@ class MergeRequestsController < ProjectResourceController
# Allow modify merge_request
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
- # Allow destroy merge_request
- before_filter :authorize_admin_merge_request!, only: [:destroy]
-
def index
@merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute
end
@@ -90,14 +87,6 @@ class MergeRequestsController < ProjectResourceController
end
end
- def destroy
- @merge_request.destroy
-
- respond_to do |format|
- format.html { redirect_to project_merge_requests_url(@project) }
- end
- end
-
def branch_from
@commit = project.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
@@ -108,6 +97,13 @@ class MergeRequestsController < ProjectResourceController
@commit = CommitDecorator.decorate(@commit)
end
+ def ci_status
+ status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha)
+ response = { status: status }
+
+ render json: response
+ end
+
protected
def merge_request
diff --git a/app/controllers/milestones_controller.rb b/app/controllers/milestones_controller.rb
index fadfee2d..a0c824e8 100644
--- a/app/controllers/milestones_controller.rb
+++ b/app/controllers/milestones_controller.rb
@@ -12,11 +12,12 @@ class MilestonesController < ProjectResourceController
def index
@milestones = case params[:f]
- when 'all'; @project.milestones
- else @project.milestones.active
+ when 'all'; @project.milestones.order("closed, due_date DESC")
+ when 'closed'; @project.milestones.closed.order("due_date DESC")
+ else @project.milestones.active.order("due_date ASC")
end
- @milestones = @milestones.includes(:project).order("due_date")
+ @milestones = @milestones.includes(:project)
@milestones = @milestones.page(params[:page]).per(20)
end
@@ -42,6 +43,7 @@ class MilestonesController < ProjectResourceController
def create
@milestone = @project.milestones.new(params[:milestone])
+ @milestone.author_id_of_changes = current_user.id
if @milestone.save
redirect_to project_milestone_path(@project, @milestone)
@@ -51,7 +53,7 @@ class MilestonesController < ProjectResourceController
end
def update
- @milestone.update_attributes(params[:milestone])
+ @milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id))
respond_to do |format|
format.js
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 2fb783b2..c4ebf0e4 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -1,5 +1,5 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
- Gitlab.config.omniauth_providers.each do |provider|
+ Gitlab.config.omniauth.providers.each do |provider|
define_method provider['name'] do
handle_omniauth
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 1165729f..17b0921b 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -46,6 +46,10 @@ class ProjectsController < ProjectResourceController
format.js
end
end
+
+ rescue Project::TransferError => ex
+ @error = ex
+ render :update_failed
end
def show
@@ -54,12 +58,12 @@ class ProjectsController < ProjectResourceController
respond_to do |format|
format.html do
- unless @project.empty_repo?
- @last_push = current_user.recent_push(@project.id)
- render :show
- else
- render "projects/empty"
- end
+ unless @project.empty_repo?
+ @last_push = current_user.recent_push(@project.id)
+ render :show
+ else
+ render "projects/empty"
+ end
end
format.js
end
@@ -86,12 +90,18 @@ class ProjectsController < ProjectResourceController
end
def graph
- graph = Gitlab::Graph::JsonBuilder.new(project)
-
- @days_json, @commits_json = graph.days_json, graph.commits_json
+ respond_to do |format|
+ format.html
+ format.json do
+ graph = Gitlab::Graph::JsonBuilder.new(project)
+ render :json => graph.to_json
+ end
+ end
end
def destroy
+ return access_denied! unless can?(current_user, :remove_project, project)
+
# Disable the UsersProject update_repository call, otherwise it will be
# called once for every person removed from the project
UsersProject.skip_callback(:destroy, :after, :update_repository)
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index b0438222..977524a4 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -16,7 +16,7 @@ class SnippetsController < ProjectResourceController
respond_to :html
def index
- @snippets = @project.snippets
+ @snippets = @project.snippets.fresh
end
def new
@@ -62,7 +62,7 @@ class SnippetsController < ProjectResourceController
redirect_to project_snippets_path(@project)
end
- def raw
+ def raw
send_data(
@snippet.content,
type: "text/plain",
diff --git a/app/decorators/commit_decorator.rb b/app/decorators/commit_decorator.rb
index 69d5b178..a066b2e4 100644
--- a/app/decorators/commit_decorator.rb
+++ b/app/decorators/commit_decorator.rb
@@ -76,7 +76,7 @@ class CommitDecorator < ApplicationDecorator
source_name = send "#{options[:source]}_name".to_sym
source_email = send "#{options[:source]}_email".to_sym
text = if options[:avatar]
- avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size]
+ avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: ""
%Q{#{avatar} #{source_name}}
else
source_name
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index a689213b..52715a26 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,4 +1,5 @@
require 'digest/md5'
+require 'uri'
module ApplicationHelper
@@ -30,13 +31,15 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
- def gravatar_icon(user_email = '', size = 40)
- if Gitlab.config.disable_gravatar? || user_email.blank?
+ def gravatar_icon(user_email = '', size = nil)
+ size = 40 if size.nil? || size <= 0
+
+ if !Gitlab.config.gravatar.enabled || user_email.blank?
'no_avatar.png'
else
- gravatar_prefix = request.ssl? ? "https://secure" : "http://www"
+ gravatar_url = request.ssl? ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
user_email.strip!
- "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=mm"
+ sprintf(gravatar_url, {:hash => Digest::MD5.hexdigest(user_email.downcase), :email => URI.escape(user_email), :size => size})
end
end
@@ -45,7 +48,7 @@ module ApplicationHelper
end
def web_app_url
- "#{request_protocol}://#{Gitlab.config.web_host}/"
+ "#{request_protocol}://#{Gitlab.config.gitlab.host}/"
end
def last_commit(project)
@@ -92,6 +95,7 @@ module ApplicationHelper
{ label: "API Help", url: help_api_path },
{ label: "Markdown Help", url: help_markdown_path },
{ label: "SSH Keys Help", url: help_ssh_path },
+ { label: "Gitlab Rake Tasks Help", url: help_raketasks_path },
]
project_nav = []
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 99ea9ef2..2825787f 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -4,28 +4,6 @@ module IssuesHelper
project_issues_path project, params
end
- def link_to_issue_assignee(issue)
- project = issue.project
-
- tm = project.team_member_by_id(issue.assignee_id)
- if tm
- link_to issue.assignee_name, project_team_member_path(project, tm), class: "author_link"
- else
- issue.assignee_name
- end
- end
-
- def link_to_issue_author(issue)
- project = issue.project
-
- tm = project.team_member_by_id(issue.author_id)
- if tm
- link_to issue.author_name, project_team_member_path(project, tm), class: "author_link"
- else
- issue.author_name
- end
- end
-
def issue_css_classes issue
classes = "issue"
classes << " closed" if issue.closed
@@ -52,4 +30,14 @@ module IssuesHelper
open: "open"
}
end
+
+ def labels_autocomplete_source
+ labels = @project.issues_labels.order('count DESC')
+ labels = labels.map{ |l| { label: l.name, value: l.name } }
+ labels.to_json
+ end
+
+ def issues_active_milestones
+ @project.milestones.active.order("id desc").all
+ end
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index b23c4a8f..f48425bd 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -1,26 +1,4 @@
module MergeRequestsHelper
- def link_to_merge_request_assignee(merge_request)
- project = merge_request.project
-
- tm = project.team_member_by_id(merge_request.assignee_id)
- if tm
- link_to merge_request.assignee_name, project_team_member_path(project, tm), class: "author_link"
- else
- merge_request.assignee_name
- end
- end
-
- def link_to_merge_request_author(merge_request)
- project = merge_request.project
-
- tm = project.team_member_by_id(merge_request.author_id)
- if tm
- link_to merge_request.author_name, project_team_member_path(project, tm), class: "author_link"
- else
- merge_request.author_name
- end
- end
-
def new_mr_path_from_push_event(event)
new_project_merge_request_path(
event.project,
@@ -39,7 +17,7 @@ module MergeRequestsHelper
classes
end
- def ci_status_path
- @project.gitlab_ci_service.commit_badge_path(@merge_request.last_commit.sha)
+ def ci_build_details_path merge_request
+ merge_request.project.gitlab_ci_service.build_page(merge_request.last_commit.sha)
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 7c302ef4..425dd471 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -8,11 +8,49 @@ module ProjectsHelper
end
def link_to_project project
- link_to project.name, project
+ link_to project do
+ title = content_tag(:strong, project.name)
+
+ if project.namespace
+ namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny')
+ title = namespace + title
+ end
+
+ title
+ end
+ end
+
+ def link_to_member(project, author)
+ return "(deleted)" unless author
+
+ # Build avatar image tag
+ avatar = image_tag(gravatar_icon(author.try(:email)), width: 16, class: "lil_av")
+
+ # Build name strong tag
+ name = content_tag :strong, author.name, class: 'author'
+
+ author_html = avatar + name
+
+ tm = project.team_member_by_id(author)
+
+ content_tag :span, class: 'member-link' do
+ if tm
+ link_to author_html, project_team_member_path(project, tm), class: "author_link"
+ else
+ author_html
+ end
+ end
end
def tm_path team_member
project_team_member_path(@project, team_member)
end
-end
+ def project_title project
+ if project.group
+ project.name_with_namespace
+ else
+ project.name
+ end
+ end
+end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index a4bec87c..d52d8af6 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -72,7 +72,7 @@ module TabHelper
return "active" if current_page?(controller: "projects", action: action, id: @project)
end
- if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
+ if ['snippets', 'services', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
"active"
end
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 29cebada..5cd9b829 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -3,11 +3,11 @@ class Notify < ActionMailer::Base
add_template_helper ApplicationHelper
add_template_helper GitlabMarkdownHelper
- default_url_options[:host] = Gitlab.config.web_host
- default_url_options[:protocol] = Gitlab.config.web_protocol
- default_url_options[:port] = Gitlab.config.web_port if Gitlab.config.web_custom_port?
+ default_url_options[:host] = Gitlab.config.gitlab.host
+ default_url_options[:protocol] = Gitlab.config.gitlab.protocol
+ default_url_options[:port] = Gitlab.config.gitlab.port if Gitlab.config.gitlab_on_non_standard_port?
- default from: Gitlab.config.email_from
+ default from: Gitlab.config.gitlab.email_from
@@ -31,6 +31,7 @@ class Notify < ActionMailer::Base
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@issue = Issue.find issue_id
@issue_status = status
+ @project = @issue.project
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
subject: subject("changed issue ##{@issue.id}", @issue.title))
@@ -102,6 +103,12 @@ class Notify < ActionMailer::Base
end
+ def project_was_moved_email(user_project_id)
+ @users_project = UsersProject.find user_project_id
+ @project = @users_project.project
+ mail(to: @users_project.user.email,
+ subject: subject("project was moved"))
+ end
#
# User
diff --git a/app/models/ability.rb b/app/models/ability.rb
index b09899f1..2d80c672 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -17,9 +17,7 @@ class Ability
# Rules based on role in project
if project.master_access_for?(user)
- # TODO: replace with master rules.
- # Only allow project administration for namespace owners
- rules << project_admin_rules
+ rules << project_master_rules
elsif project.dev_access_for?(user)
rules << project_dev_rules
@@ -93,13 +91,16 @@ class Ability
:admin_merge_request,
:admin_note,
:accept_mr,
- :admin_wiki
+ :admin_wiki,
+ :admin_project
]
end
def project_admin_rules
project_master_rules + [
- :admin_project
+ :change_namespace,
+ :rename_project,
+ :remove_project
]
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 200c915a..f11b7fe0 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -87,14 +87,10 @@ class Commit
last = project.commit(from.try(:strip))
if first && last
- commits = [first, last].sort_by(&:created_at)
- younger = commits.first
- older = commits.last
-
- result[:same] = (younger.id == older.id)
- result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
- result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
- result[:commit] = Commit.new(older)
+ result[:same] = (first.id == last.id)
+ result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
+ result[:diffs] = project.repo.diff(last.id, first.id) rescue []
+ result[:commit] = Commit.new(first)
end
result
@@ -163,6 +159,8 @@ class Commit
while !lines.first.start_with?("diff --git") do
lines.shift
end
+ lines.pop if lines.last =~ /^[\d.]+$/ # Git version
+ lines.pop if lines.last == "-- " # end of diff
lines.join("\n")
end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 2b92783c..90376e73 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -15,6 +15,7 @@
#
class Event < ActiveRecord::Base
+ include NoteEvent
include PushEvent
attr_accessible :project, :action, :data, :author_id, :project_id,
@@ -58,12 +59,14 @@ class Event < ActiveRecord::Base
end
end
- # Next events currently enabled for system
- # - push
- # - new issue
- # - merge request
- def allowed?
- push? || issue? || merge_request? || membership_changed?
+ def proper?
+ if push?
+ true
+ elsif membership_changed?
+ true
+ else
+ (issue? || merge_request? || note? || milestone?) && target
+ end
end
def project_name
@@ -94,6 +97,14 @@ class Event < ActiveRecord::Base
action == self.class::Reopened
end
+ def milestone?
+ target_type == "Milestone"
+ end
+
+ def note?
+ target_type == "Note"
+ end
+
def issue?
target_type == "Issue"
end
diff --git a/app/models/gitlab_ci_service.rb b/app/models/gitlab_ci_service.rb
index 24b70323..a2f5634a 100644
--- a/app/models/gitlab_ci_service.rb
+++ b/app/models/gitlab_ci_service.rb
@@ -36,4 +36,22 @@ class GitlabCiService < Service
def commit_badge_path sha
project_url + "/status?sha=#{sha}"
end
+
+ def commit_status_path sha
+ project_url + "/builds/#{sha}/status.json?token=#{token}"
+ end
+
+ def commit_status sha
+ response = HTTParty.get(commit_status_path(sha))
+
+ if response.code == 200 and response["status"]
+ response["status"]
+ else
+ :error
+ end
+ end
+
+ def build_page sha
+ project_url + "/builds/#{sha}"
+ end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 8039813a..052e0850 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -204,7 +204,7 @@ class MergeRequest < ActiveRecord::Base
def mr_and_commit_notes
commit_ids = commits.map(&:id)
- Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND noteable_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
+ Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
end
# Returns the raw diff for this merge request
@@ -220,4 +220,8 @@ class MergeRequest < ActiveRecord::Base
def to_patch
project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}")
end
+
+ def last_commit_short_sha
+ @last_commit_short_sha ||= last_commit.sha[0..10]
+ end
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index a50831a2..4fac9bec 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -13,18 +13,26 @@
#
class Milestone < ActiveRecord::Base
- attr_accessible :title, :description, :due_date, :closed
+ attr_accessible :title, :description, :due_date, :closed, :author_id_of_changes
+ attr_accessor :author_id_of_changes
belongs_to :project
has_many :issues
has_many :merge_requests
+ scope :active, where(closed: false)
+ scope :closed, where(closed: true)
+
validates :title, presence: true
validates :project, presence: true
validates :closed, inclusion: { in: [true, false] }
- def self.active
- where("due_date > ? OR due_date IS NULL", Date.today)
+ def expired?
+ if due_date
+ due_date < Date.today
+ else
+ false
+ end
end
def participants
@@ -52,4 +60,20 @@ class Milestone < ActiveRecord::Base
def expires_at
"expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
end
+
+ def can_be_closed?
+ open? && issues.opened.count.zero?
+ end
+
+ def is_empty?
+ total_items_count.zero?
+ end
+
+ def open?
+ !closed
+ end
+
+ def author_id
+ author_id_of_changes
+ end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index e1c24de9..8c90f5ae 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -48,23 +48,30 @@ class Namespace < ActiveRecord::Base
end
def ensure_dir_exist
- namespace_dir_path = File.join(Gitlab.config.git_base_path, path)
+ namespace_dir_path = File.join(Gitlab.config.gitolite.repos_path, path)
system("mkdir -m 770 #{namespace_dir_path}") unless File.exists?(namespace_dir_path)
end
def move_dir
if path_changed?
- old_path = File.join(Gitlab.config.git_base_path, path_was)
- new_path = File.join(Gitlab.config.git_base_path, path)
+ old_path = File.join(Gitlab.config.gitolite.repos_path, path_was)
+ new_path = File.join(Gitlab.config.gitolite.repos_path, path)
if File.exists?(new_path)
raise "Already exists"
end
- system("mv #{old_path} #{new_path}")
+
+ if system("mv #{old_path} #{new_path}")
+ send_update_instructions
+ end
end
end
def rm_dir
- dir_path = File.join(Gitlab.config.git_base_path, path)
+ dir_path = File.join(Gitlab.config.gitolite.repos_path, path)
system("rm -rf #{dir_path}")
end
+
+ def send_update_instructions
+ projects.each(&:send_move_instructions)
+ end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index a8ae9080..b62b3fe6 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -19,7 +19,7 @@ require 'file_size_validator'
class Note < ActiveRecord::Base
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
- :attachment, :line_code
+ :attachment, :line_code, :commit_id
attr_accessor :notify
attr_accessor :notify_author
@@ -35,10 +35,14 @@ class Note < ActiveRecord::Base
validates :line_code, format: { with: /\A\d+_\d+_\d+\Z/ }, allow_blank: true
validates :attachment, file_size: { maximum: 10.megabytes.to_i }
+ validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
+ validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
+
mount_uploader :attachment, AttachmentUploader
# Scopes
- scope :common, ->{ where(noteable_id: nil) }
+ scope :for_commits, ->{ where(noteable_type: "Commit") }
+ scope :common, ->{ where(noteable_id: nil, commit_id: nil) }
scope :today, ->{ where("created_at >= :date", date: Date.today) }
scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) }
scope :since, ->(day) { where("created_at >= :date", date: (day)) }
@@ -122,7 +126,7 @@ class Note < ActiveRecord::Base
# override to return commits, which are not active record
def noteable
if for_commit?
- project.commit(noteable_id)
+ project.commit(commit_id)
else
super
end
@@ -151,4 +155,12 @@ class Note < ActiveRecord::Base
def votable?
for_issue? || (for_merge_request? && !for_diff_line?)
end
+
+ def noteable_type_name
+ if noteable_type.present?
+ noteable_type.downcase
+ else
+ "wall"
+ end
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index ac315c49..251f4975 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -25,6 +25,9 @@ class Project < ActiveRecord::Base
include PushObserver
include Authority
include Team
+ include NamespacedProject
+
+ class TransferError < StandardError; end
attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
:wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
@@ -36,6 +39,10 @@ class Project < ActiveRecord::Base
# Relations
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
belongs_to :namespace
+
+ # TODO: replace owner with creator.
+ # With namespaces a project owner will be a namespace owner
+ # so this field makes sense only for global projects
belongs_to :owner, class_name: "User"
has_many :users, through: :users_projects
has_many :events, dependent: :destroy
@@ -97,7 +104,7 @@ class Project < ActiveRecord::Base
namespace_id = Namespace.find_by_path(id.first).id
where(namespace_id: namespace_id).find_by_path(id.last)
else
- find_by_path(id)
+ where(path: id, namespace_id: nil).last
end
end
@@ -172,7 +179,7 @@ class Project < ActiveRecord::Base
end
def repo_name
- denied_paths = %w(gitolite-admin groups projects dashboard)
+ denied_paths = %w(gitolite-admin admin dashboard groups help profile projects search)
if denied_paths.include?(path)
errors.add(:path, "like #{path} is not allowed")
@@ -188,7 +195,7 @@ class Project < ActiveRecord::Base
end
def web_url
- [Gitlab.config.url, path].join("/")
+ [Gitlab.config.gitlab.url, path_with_namespace].join("/")
end
def common_notes
@@ -196,15 +203,15 @@ class Project < ActiveRecord::Base
end
def build_commit_note(commit)
- notes.new(noteable_id: commit.id, noteable_type: "Commit")
+ notes.new(commit_id: commit.id, noteable_type: "Commit")
end
def commit_notes(commit)
- notes.where(noteable_id: commit.id, noteable_type: "Commit").where('line_code IS NULL OR line_code = ""')
+ notes.where(commit_id: commit.id, noteable_type: "Commit").where('line_code IS NULL OR line_code = ""')
end
def commit_line_notes(commit)
- notes.where(noteable_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
+ notes.where(commit_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
end
def public?
@@ -239,51 +246,11 @@ class Project < ActiveRecord::Base
gitlab_ci_service && gitlab_ci_service.active
end
- def path_with_namespace
- if namespace
- namespace.path + '/' + path
- else
- path
- end
- end
-
# For compatibility with old code
def code
path
end
- def transfer(new_namespace)
- Project.transaction do
- old_namespace = namespace
- self.namespace = new_namespace
-
- old_dir = old_namespace.try(:path) || ''
- new_dir = new_namespace.try(:path) || ''
-
- old_repo = if old_dir.present?
- File.join(old_dir, self.path)
- else
- self.path
- end
-
- Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
-
- git_host.move_repository(old_repo, self)
-
- save!
- end
- end
-
- def name_with_namespace
- @name_with_namespace ||= begin
- if namespace
- namespace.human_name + " / " + name
- else
- name
- end
- end
- end
-
def items_for entity
case entity
when 'issue' then
@@ -293,7 +260,9 @@ class Project < ActiveRecord::Base
end
end
- def namespace_owner
- namespace.try(:owner)
+ def send_move_instructions
+ self.users_projects.each do |member|
+ Notify.project_was_moved_email(member.id).deliver
+ end
end
end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 997c19bd..8d7eb788 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -22,7 +22,7 @@ class Snippet < ActiveRecord::Base
belongs_to :author, class_name: "User"
has_many :notes, as: :noteable, dependent: :destroy
- delegate :name, :email, to: :author, prefix: true
+ delegate :name, :email, to: :author, prefix: true, allow_nil: true
validates :author, presence: true
validates :project, presence: true
diff --git a/app/models/user.rb b/app/models/user.rb
index 3f2d7c92..1bc070f0 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -56,12 +56,12 @@ class User < ActiveRecord::Base
has_many :issues, foreign_key: :author_id, dependent: :destroy
has_many :notes, foreign_key: :author_id, dependent: :destroy
has_many :merge_requests, foreign_key: :author_id, dependent: :destroy
- has_many :my_own_projects, class_name: "Project", foreign_key: :owner_id
has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy
has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy
has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy
+ validates :name, presence: true
validates :bio, length: { within: 0..255 }
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
@@ -123,16 +123,4 @@ class User < ActiveRecord::Base
self.password = self.password_confirmation = Devise.friendly_token.first(8)
end
end
-
- def authorized_groups
- @authorized_groups ||= begin
- groups = Group.where(id: self.projects.pluck(:namespace_id)).all
- groups = groups + self.groups
- groups.uniq
- end
- end
-
- def authorized_projects
- Project.authorized_for(self)
- end
end
diff --git a/app/models/users_project.rb b/app/models/users_project.rb
index 6231088f..3d76a4df 100644
--- a/app/models/users_project.rb
+++ b/app/models/users_project.rb
@@ -28,6 +28,7 @@ class UsersProject < ActiveRecord::Base
validates :user, presence: true
validates :user_id, uniqueness: { :scope => [:project_id], message: "already exists in project" }
+ validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true
validates :project, presence: true
delegate :name, :email, to: :user, prefix: true
diff --git a/app/observers/activity_observer.rb b/app/observers/activity_observer.rb
index 48351bac..c188e572 100644
--- a/app/observers/activity_observer.rb
+++ b/app/observers/activity_observer.rb
@@ -1,18 +1,27 @@
class ActivityObserver < ActiveRecord::Observer
- observe :issue, :merge_request
+ observe :issue, :merge_request, :note, :milestone
def after_create(record)
- Event.create(
- project: record.project,
- target_id: record.id,
- target_type: record.class.name,
- action: Event.determine_action(record),
- author_id: record.author_id
- )
+ event_author_id = record.author_id
+
+ # Skip status notes
+ if record.kind_of?(Note) && record.note.include?("_Status changed to ")
+ return true
+ end
+
+ if event_author_id
+ Event.create(
+ project: record.project,
+ target_id: record.id,
+ target_type: record.class.name,
+ action: Event.determine_action(record),
+ author_id: event_author_id
+ )
+ end
end
def after_save(record)
- if record.changed.include?("closed")
+ if record.changed.include?("closed") && record.author_id_of_changes
Event.create(
project: record.project,
target_id: record.id,
diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb
index 9f9762ae..131336be 100644
--- a/app/observers/issue_observer.rb
+++ b/app/observers/issue_observer.rb
@@ -16,7 +16,7 @@ class IssueObserver < ActiveRecord::Observer
if status
Note.create_status_change_note(issue, current_user, status)
[issue.author, issue.assignee].compact.each do |recipient|
- Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user)
+ Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id).deliver
end
end
end
diff --git a/app/observers/note_observer.rb b/app/observers/note_observer.rb
index 083aa705..fe01efca 100644
--- a/app/observers/note_observer.rb
+++ b/app/observers/note_observer.rb
@@ -21,7 +21,7 @@ class NoteObserver < ActiveRecord::Observer
# Notifies the whole team except the author of note
def notify_team(note)
# Note: wall posts are not "attached" to anything, so fall back to "Wall"
- noteable_type = note.noteable_type || "Wall"
+ noteable_type = note.noteable_type.presence || "Wall"
notify_method = "note_#{noteable_type.underscore}_email".to_sym
if Notify.respond_to? notify_method
diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb
index bd41e51e..b1c69456 100644
--- a/app/observers/project_observer.rb
+++ b/app/observers/project_observer.rb
@@ -3,7 +3,8 @@ class ProjectObserver < ActiveRecord::Observer
project.update_repository
end
- def after_save(project)
+ def after_update(project)
+ project.send_move_instructions if project.namespace_id_changed?
end
def after_destroy(project)
diff --git a/app/roles/account.rb b/app/roles/account.rb
index 8157898f..ede12b60 100644
--- a/app/roles/account.rb
+++ b/app/roles/account.rb
@@ -47,7 +47,7 @@ module Account
end
def cared_merge_requests
- MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id).opened
+ MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id)
end
def project_ids
@@ -105,4 +105,20 @@ module Account
def namespace_id
namespace.try :id
end
+
+ def authorized_groups
+ @authorized_groups ||= begin
+ groups = Group.where(id: self.projects.pluck(:namespace_id)).all
+ groups = groups + self.groups
+ groups.uniq
+ end
+ end
+
+ def authorized_projects
+ Project.authorized_for(self)
+ end
+
+ def my_own_projects
+ Project.personal(self)
+ end
end
diff --git a/app/roles/namespaced_project.rb b/app/roles/namespaced_project.rb
new file mode 100644
index 00000000..8656890a
--- /dev/null
+++ b/app/roles/namespaced_project.rb
@@ -0,0 +1,59 @@
+module NamespacedProject
+ def transfer(new_namespace)
+ Project.transaction do
+ old_namespace = namespace
+ self.namespace = new_namespace
+
+ old_dir = old_namespace.try(:path) || ''
+ new_dir = new_namespace.try(:path) || ''
+
+ old_repo = if old_dir.present?
+ File.join(old_dir, self.path)
+ else
+ self.path
+ end
+
+ if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?
+ raise TransferError.new("Project with same path in target namespace already exists")
+ end
+
+ Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
+
+ git_host.move_repository(old_repo, self)
+
+ save!
+ end
+ rescue Gitlab::ProjectMover::ProjectMoveError => ex
+ raise TransferError.new(ex.message)
+ end
+
+ def name_with_namespace
+ @name_with_namespace ||= begin
+ if namespace
+ namespace.human_name + " / " + name
+ else
+ name
+ end
+ end
+ end
+
+ def namespace_owner
+ namespace.try(:owner)
+ end
+
+ def chief
+ if namespace
+ namespace_owner
+ else
+ owner
+ end
+ end
+
+ def path_with_namespace
+ if namespace
+ namespace.path + '/' + path
+ else
+ path
+ end
+ end
+end
diff --git a/app/roles/note_event.rb b/app/roles/note_event.rb
new file mode 100644
index 00000000..db4ced0c
--- /dev/null
+++ b/app/roles/note_event.rb
@@ -0,0 +1,37 @@
+module NoteEvent
+ def note_commit_id
+ target.commit_id
+ end
+
+ def note_short_commit_id
+ note_commit_id[0..8]
+ end
+
+ def note_commit?
+ target.noteable_type == "Commit"
+ end
+
+ def note_target
+ target.noteable
+ end
+
+ def note_target_id
+ if note_commit?
+ target.commit_id
+ else
+ target.noteable_id.to_s
+ end
+ end
+
+ def wall_note?
+ target.noteable_type.blank?
+ end
+
+ def note_target_type
+ if target.noteable_type.present?
+ target.noteable_type.titleize
+ else
+ "Wall"
+ end.downcase
+ end
+end
diff --git a/app/roles/push_observer.rb b/app/roles/push_observer.rb
index c5c5203d..dda18267 100644
--- a/app/roles/push_observer.rb
+++ b/app/roles/push_observer.rb
@@ -114,7 +114,7 @@ module PushObserver
id: commit.id,
message: commit.safe_message,
timestamp: commit.date.xmlschema,
- url: "#{Gitlab.config.url}/#{path}/commits/#{commit.id}",
+ url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",
author: {
name: commit.author_name,
email: commit.author_email
diff --git a/app/roles/repository.rb b/app/roles/repository.rb
index 74cae5c8..78190ca9 100644
--- a/app/roles/repository.rb
+++ b/app/roles/repository.rb
@@ -45,8 +45,22 @@ module Repository
end
def has_post_receive_file?
- hook_file = File.join(path_to_repo, 'hooks', 'post-receive')
- File.exists?(hook_file)
+ !!hook_file
+ end
+
+ def valid_post_receive_file?
+ valid_hook_file == hook_file
+ end
+
+ def valid_hook_file
+ @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive'))
+ end
+
+ def hook_file
+ @hook_file ||= begin
+ hook_path = File.join(path_to_repo, 'hooks', 'post-receive')
+ File.read(hook_path) if File.exists?(hook_path)
+ end
end
# Returns an Array of branch names
@@ -83,7 +97,7 @@ module Repository
end
def path_to_repo
- File.join(Gitlab.config.git_base_path, "#{path_with_namespace}.git")
+ File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git")
end
def namespace_dir
@@ -185,7 +199,7 @@ module Repository
end
def http_url_to_repo
- http_url = [Gitlab.config.url, "/", path_with_namespace, ".git"].join('')
+ http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
end
# Check if current branch name is marked as protected in the system
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
deleted file mode 100644
index 67516eb2..00000000
--- a/app/views/admin/groups/_form.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-= form_for [:admin, @group] do |f|
- - if @group.errors.any?
- .alert-message.block-message.error
- %span= @group.errors.full_messages.first
- .clearfix.group_name_holder
- = f.label :name do
- Group name is
- .input
- = f.text_field :name, placeholder: "Example Group", class: "xxlarge"
-
- .form-actions
- = f.submit 'Save group', class: "btn save-btn"
diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml
index 9904122c..901d07e7 100644
--- a/app/views/admin/groups/edit.html.haml
+++ b/app/views/admin/groups/edit.html.haml
@@ -1,3 +1,28 @@
-%h3.page_title Edit Group
-%br
-= render 'form'
+%h3.page_title Rename Group
+%hr
+= form_for [:admin, @group] do |f|
+ - if @group.errors.any?
+ .alert-message.block-message.error
+ %span= @group.errors.full_messages.first
+ .clearfix.group_name_holder
+ = f.label :name do
+ Group name is
+ .input
+ = f.text_field :name, placeholder: "Example Group", class: "xxlarge"
+
+
+
+ .clearfix.group_name_holder
+ = f.label :path do
+ %span.cred Group path is
+ .input
+ = f.text_field :path, placeholder: "example-group", class: "xxlarge danger"
+ %ul.cred
+ %li Changing group path can have unintended side effects.
+ %li Renaming group path will rename directory for all related projects
+ %li It will change web url for access group and group projects.
+ %li It will change the git path to repositories under this group.
+
+ .form-actions
+ = f.submit 'Rename group', class: "btn danger"
+ = link_to 'Cancel', admin_groups_path, class: "btn cancel-btn"
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 952d5151..49acedc8 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -12,17 +12,24 @@
%table
%thead
- %th Name
- %th Path
- %th Projects
- %th Edit
- %th.cred Danger Zone!
+ %tr
+ %th
+ Name
+ %i.icon-sort-down
+ %th Path
+ %th Projects
+ %th Owner
+ %th.cred Danger Zone!
- @groups.each do |group|
%tr
- %td= link_to group.name, [:admin, group]
+ %td
+ %strong= link_to group.name, [:admin, group]
%td= group.path
%td= group.projects.count
- %td= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small"
- %td.bgred= link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger"
+ %td
+ = link_to group.owner_name, admin_user_path(group.owner_id)
+ %td.bgred
+ = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small"
+ = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger"
= paginate @groups, theme: "admin"
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index d371acad..41f6d9b3 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -1,8 +1,5 @@
%h3.page_title
Group: #{@group.name}
- = link_to edit_admin_group_path(@group), class: "btn right" do
- %i.icon-edit
- Edit
%br
%table.zebra-striped
@@ -16,36 +13,64 @@
Name:
%td
= @group.name
+
+ = link_to edit_admin_group_path(@group), class: "btn btn-small right" do
+ %i.icon-edit
+ Rename
%tr
%td
%b
Path:
%td
- %span.monospace= File.join(Gitlab.config.git_base_path, @group.path)
+ %span.monospace= File.join(Gitlab.config.gitolite.repos_path, @group.path)
%tr
%td
%b
Owner:
%td
= @group.owner_name
-.ui-box
- %h5
- Projects
- %small
- (#{@group.projects.count})
- %ul.unstyled
+ .right
+ = link_to "#", class: "btn btn-small change-owner-link" do
+ %i.icon-edit
+ Change owner
+
+ %tr.change-owner-holder.hide
+ %td.bgred
+ %b.cred
+ New Owner:
+ %td.bgred
+ = form_for [:admin, @group] do |f|
+ = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
+ %div
+ = f.submit 'Change Owner', class: "btn danger"
+ = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
+%fieldset
+ %legend Projects (#{@group.projects.count})
+ %table
+ %thead
+ %tr
+ %th Project name
+ %th Path
+ %th Users
+ %th.cred Danger Zone!
- @group.projects.each do |project|
- %li.wll
- %strong
- = link_to project.name, [:admin, project]
- .right
- = link_to 'Remove from group', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
- .clearfix
+ %tr
+ %td
+ = link_to project.name_with_namespace, [:admin, project]
+ %td
+ %span.monospace= project.path_with_namespace + ".git"
+ %td= project.users.count
+ %td.bgred
+ = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
= form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do
%fieldset
%legend Move projects to group
+ .alert
+ You can move only projects with existing repos
+ %br
+ Group projects will be moved in group directory and will not be accessible by old path
.clearfix
= label_tag :project_ids do
Projects
@@ -53,3 +78,17 @@
= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
.form-actions
= submit_tag 'Add', class: "btn primary"
+
+:javascript
+ $(function(){
+ var modal = $('.change-owner-holder');
+ $('.change-owner-link').bind("click", function(){
+ $(this).hide();
+ modal.show();
+ });
+ $('.change-owner-cancel-link').bind("click", function(){
+ modal.hide();
+ $('.change-owner-link').show();
+ })
+ })
+
diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml
index e33c5468..25644d63 100644
--- a/app/views/admin/logs/show.html.haml
+++ b/app/views/admin/logs/show.html.haml
@@ -3,6 +3,8 @@
= link_to "githost.log", "#githost", 'data-toggle' => 'tab'
%li
= link_to "application.log", "#application", 'data-toggle' => 'tab'
+ %li
+ = link_to "production.log", "#production", 'data-toggle' => 'tab'
%p.light To prevent perfomance issues admin logs output the last 2000 lines
.tab-content
@@ -34,3 +36,17 @@
- Gitlab::AppLogger.read_latest.each do |line|
%li
%p= line
+ .tab-pane#production
+ .file_holder#README
+ .file_title
+ %i.icon-file
+ production.log
+ .right
+ = link_to '#', class: 'log-bottom' do
+ %i.icon-arrow-down
+ Scroll down
+ .file_content.logs
+ %ol
+ - Gitlab::Logger.read_latest_for('production.log').each do |line|
+ %li
+ %p= line
diff --git a/app/views/admin/projects/_form.html.haml b/app/views/admin/projects/_form.html.haml
index e515c68c..27c22872 100644
--- a/app/views/admin/projects/_form.html.haml
+++ b/app/views/admin/projects/_form.html.haml
@@ -19,40 +19,47 @@
.input
= text_field_tag :ppath, @project.path_to_repo, class: "xlarge", disabled: true
- - unless project.new_record?
+ - if project.repo_exists?
.clearfix
- = f.label :namespace_id
- .input= f.select :namespace_id, namespaces_options(@project.namespace_id), {}, {class: 'chosen'}
+ = f.label :default_branch, "Default Branch"
+ .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;")
- - if project.repo_exists?
- .clearfix
- = f.label :default_branch, "Default Branch"
- .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;")
+ %fieldset.adv_settings
+ %legend Features:
- - unless project.new_record?
- %fieldset.adv_settings
- %legend Features:
+ .clearfix
+ = f.label :issues_enabled, "Issues"
+ .input= f.check_box :issues_enabled
- .clearfix
- = f.label :issues_enabled, "Issues"
- .input= f.check_box :issues_enabled
+ .clearfix
+ = f.label :merge_requests_enabled, "Merge Requests"
+ .input= f.check_box :merge_requests_enabled
- .clearfix
- = f.label :merge_requests_enabled, "Merge Requests"
- .input= f.check_box :merge_requests_enabled
+ .clearfix
+ = f.label :wall_enabled, "Wall"
+ .input= f.check_box :wall_enabled
- .clearfix
- = f.label :wall_enabled, "Wall"
- .input= f.check_box :wall_enabled
+ .clearfix
+ = f.label :wiki_enabled, "Wiki"
+ .input= f.check_box :wiki_enabled
- .clearfix
- = f.label :wiki_enabled, "Wiki"
- .input= f.check_box :wiki_enabled
+ %fieldset.features
+ %legend Transfer:
+ .control-group
+ = f.label :namespace_id do
+ %span Namespace
+ .controls
+ = f.select :namespace_id, namespaces_options(@project.namespace_id, :all), {}, {class: 'chosen'}
+ %br
+ %ul.prepend-top-10.cred
+ %li Be careful. Changing project namespace can have unintended side effects
+ %li You can transfer project only to namespaces you can manage
+ %li You will need to update your local repositories to point to the new location.
- - unless project.new_record?
- .actions
- = f.submit 'Save Project', class: "btn save-btn"
- = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn"
+
+ .actions
+ = f.submit 'Save Project', class: "btn save-btn"
+ = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn"
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 9bbcbc71..310cfa53 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,5 +1,5 @@
%h3.page_title
- Projects
+ Projects (#{@projects.count})
= link_to 'New Project', new_project_path, class: "btn small right"
%br
= form_tag admin_projects_path, method: :get, class: 'form-inline' do
@@ -9,12 +9,15 @@
%table
%thead
- %th Name
- %th Path
- %th Team Members
- %th Last Commit
- %th Edit
- %th.cred Danger Zone!
+ %tr
+ %th
+ Name
+ %i.icon-sort-down
+ %th Path
+ %th Team Members
+ %th Last Commit
+ %th Edit
+ %th.cred Danger Zone!
- @projects.each do |project|
%tr
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 47185308..634b1836 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -4,14 +4,24 @@
%i.icon-edit
Edit
-- if !@project.has_post_receive_file? && @project.has_commits?
- %br
- .alert.alert-error
- %span
- %strong Important!
- Project has commits but missing post-receive file.
- %br
- If you exported project manually - copy post-receive hook to bare repository
+- if @project.has_commits?
+ - if !@project.has_post_receive_file?
+ %br
+ .alert.alert-error
+ %span
+ %strong Project has commits but missing post-receive file.
+ %br
+ If you exported project manually - make a link of post-receive hook file from gitolite to project repository
+ - elsif !@project.valid_post_receive_file?
+ %br
+ .alert.alert-error
+ %span
+ %strong Project has invalid post-receive file.
+ %br
+ 1. Make sure your gitolite instace has latest post-receive file.
+ %br
+ 2. Make a link of post-receive hook file from gitolite to project repository
+
%br
%table.zebra-striped
@@ -37,23 +47,63 @@
%tr
%td
%b
- Path:
+ Owned by:
%td
- %code= @project.path_to_repo
+ - if @project.chief
+ = link_to @project.chief.name, admin_user_path(@project.chief)
+ - else
+ (deleted)
%tr
%td
%b
Created by:
%td
= @project.owner_name || '(deleted)'
+ %tr
+ %td
+ %b
+ Created at:
+ %td
+ = @project.created_at.stamp("March 1, 1999")
+
+%table.zebra-striped
+ %thead
+ %tr
+ %th Repository
+ %th
+ %tr
+ %td
+ %b
+ FS Path:
+ %td
+ %code= @project.path_to_repo
+ %tr
+ %td
+ %b
+ Smart HTTP:
+ %td
+ = link_to @project.http_url_to_repo
+ %tr
+ %td
+ %b
+ SSH:
+ %td
+ = link_to @project.ssh_url_to_repo
+ %tr
+ %td
+ %b
+ Last commit at:
+ %td
+ = last_commit(@project)
%tr
%td
%b
Post Receive File:
%td
= check_box_tag :post_receive_file, 1, @project.has_post_receive_file?, disabled: true
+
%br
-%h3
+%h5
Team
%small
(#{@project.users_projects.count})
@@ -75,7 +125,7 @@
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
%br
-%h3 Add new team member
+%h5 Add new team member
%br
= form_tag team_update_admin_project_path(@project), class: "bulk_import", method: :put do
%table.zebra-striped
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 5d0f6fe1..1df4f590 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,5 +1,5 @@
%h3.page_title
- Users
+ Users (#{@admin_users.count})
= link_to 'New User', new_admin_user_path, class: "btn small right"
%br
@@ -21,13 +21,16 @@
%table
%thead
- %th Admin
- %th Name
- %th Username
- %th Email
- %th Projects
- %th Edit
- %th.cred Danger Zone!
+ %tr
+ %th Admin
+ %th
+ Name
+ %i.icon-sort-down
+ %th Username
+ %th Email
+ %th Projects
+ %th Edit
+ %th.cred Danger Zone!
- @admin_users.each do |user|
%tr
@@ -38,10 +41,13 @@
%td= user.users_projects.count
%td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small"
%td.bgred
- - if user.blocked
- = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success"
+ - if user == current_user
+ %span.cred It's you!
- else
- = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger"
- = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger"
+ - if user.blocked
+ = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success"
+ - else
+ = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger"
+ = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger"
= paginate @admin_users, theme: "admin"
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 6a42f787..852aead7 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -37,6 +37,12 @@
%b
Blocked:
%td= check_box_tag "blocked", 1, @admin_user.blocked, disabled: :disabled
+ %tr
+ %td
+ %b
+ Created at:
+ %td
+ = @admin_user.created_at.stamp("March 1, 1999")
%tr
%td
%b
@@ -66,7 +72,7 @@
= @admin_user.twitter
%br
-%h3 Add User to Projects
+%h5 Add User to Projects
%br
= form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do
%table
@@ -76,7 +82,7 @@
%th Project Access:
%tr
- %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
+ %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
%tr
@@ -86,8 +92,22 @@
%strong= link_to "here", help_permissions_path, class: "vlink"
%br
+- if @admin_user.groups.present?
+ %h5 Owner of groups:
+ %br
+
+ %table.zebra-striped
+ %thead
+ %tr
+ %th Name
+
+ - @admin_user.groups.each do |group|
+ %tr
+ %td= link_to group.name, admin_group_path(group)
+
+
- if @admin_user.projects.present?
- %h3 Projects
+ %h5 Projects:
%br
%table.zebra-striped
@@ -101,7 +121,7 @@
- @admin_user.users_projects.each do |tm|
- project = tm.project
%tr
- %td= link_to project.name, admin_project_path(project)
+ %td= link_to project.name_with_namespace, admin_project_path(project)
%td= tm.project_access_human
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml
index c3c7d49c..c9217989 100644
--- a/app/views/commits/_commits.html.haml
+++ b/app/views/commits/_commits.html.haml
@@ -3,4 +3,4 @@
%h5.small
%i.icon-calendar
= day.stamp("28 Aug, 2010")
- %ul.unstyled= render commits
+ %ul.well-list= render commits
diff --git a/app/views/compare/_form.html.haml b/app/views/compare/_form.html.haml
index 07f1c818..7e3a2a0e 100644
--- a/app/views/compare/_form.html.haml
+++ b/app/views/compare/_form.html.haml
@@ -1,23 +1,30 @@
%div
- %p.slead
- Fill input field with commit id like
- %code.label_branch 4eedf23
- or branch/tag name like
- %code.label_branch master
- and press compare button for commits list, code diff.
+ - unless params[:to]
+ %p.slead
+ Fill input field with commit id like
+ %code.label_branch 4eedf23
+ or branch/tag name like
+ %code.label_branch master
+ and press compare button for commits list, code diff.
- %br
+ %br
= form_tag project_compare_index_path(@project), method: :post do
.clearfix
- = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
- = "..."
- = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge"
+ .pull-left
+ - if params[:to] && params[:from]
+ = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'}
+ = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
+ = "..."
+ = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge"
+ .pull-left
+
+ = submit_tag "Compare", class: "btn primary wide commits-compare-btn"
- if @refs_are_same
.alert
%span Refs are the same
- .actions
- = submit_tag "Compare", class: "btn primary wide commits-compare-btn"
+
+
:javascript
$(function() {
diff --git a/app/views/compare/show.html.haml b/app/views/compare/show.html.haml
index 528c8b44..2abbd3fc 100644
--- a/app/views/compare/show.html.haml
+++ b/app/views/compare/show.html.haml
@@ -9,7 +9,7 @@
- if @commits.present?
%div.ui-box
%h5.small Commits (#{@commits.count})
- %ul.unstyled= render @commits
+ %ul.well-list= render @commits
- unless @diffs.empty?
%h4 Diff
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
new file mode 100644
index 00000000..c63ef24f
--- /dev/null
+++ b/app/views/dashboard/_activities.html.haml
@@ -0,0 +1,13 @@
+= render "events/event_last_push", event: @last_push
+
+.event_filter
+ = event_filter_link EventFilter.push, 'Push events'
+ = event_filter_link EventFilter.merged, 'Merge events'
+ = event_filter_link EventFilter.comments, 'Comments'
+ = event_filter_link EventFilter.team, 'Team'
+
+- if @events.any?
+ .content_list= render @events
+- else
+ %p.nothing_here_message Projects activity will be displayed here
+.loading.hide
diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml
index 8f667420..9e3401e5 100644
--- a/app/views/dashboard/_groups.html.haml
+++ b/app/views/dashboard/_groups.html.haml
@@ -8,11 +8,11 @@
= link_to new_admin_group_path, class: "btn very_small info" do
%i.icon-plus
New Group
- %ul.unstyled
+ %ul.well-list
- groups.each do |group|
- %li.wll
+ %li
= link_to group_path(id: group.path), class: dom_class(group) do
- %strong.group_name= truncate(group.name, length: 25)
+ %strong.well-title= truncate(group.name, length: 35)
%span.arrow
→
%span.last_activity
diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml
index fac0a074..cffafb54 100644
--- a/app/views/dashboard/_projects.html.haml
+++ b/app/views/dashboard/_projects.html.haml
@@ -16,18 +16,21 @@
= nav_tab :scope, 'joined' do
= link_to "Joined", dashboard_path(scope: 'joined')
- %ul.unstyled
+ %ul.well-list
- projects.each do |project|
- %li.wll
+ %li
= link_to project_path(project), class: dom_class(project) do
- if project.namespace
= project.namespace.human_name
\/
- %strong.project_name
+ %strong.well-title
= truncate(project.name, length: 25)
%span.arrow
→
%span.last_activity
%strong Last activity:
%span= project_last_activity(project)
+ - if projects.blank?
+ %li
+ %h3.nothing_here_message There are no projects here.
.bottom= paginate projects, theme: "gitlab"
diff --git a/app/views/dashboard/_sidebar.html.haml b/app/views/dashboard/_sidebar.html.haml
new file mode 100644
index 00000000..ca57cd30
--- /dev/null
+++ b/app/views/dashboard/_sidebar.html.haml
@@ -0,0 +1,14 @@
+- if @groups.present?
+ = render "groups", groups: @groups
+= render "projects", projects: @projects
+%div
+ %span.rss-icon
+ = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
+ = image_tag "rss_ui.png", title: "feed"
+ %strong News Feed
+
+%hr
+.gitlab-promo
+ = link_to "Homepage", "http://gitlabhq.com"
+ = link_to "Blog", "http://blog.gitlabhq.com"
+ = link_to "@gitlabhq", "https://twitter.com/gitlabhq"
diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml
new file mode 100644
index 00000000..d1676ed1
--- /dev/null
+++ b/app/views/dashboard/_zero_authorized_projects.html.haml
@@ -0,0 +1,12 @@
+%h3.nothing_here_message
+ There are no projects you have access to.
+ %br
+ - if current_user.can_create_project?
+ You can create up to
+ = current_user.projects_limit
+ projects. Click on button below to add a new one
+ .link_holder
+ = link_to new_project_path, class: "btn primary" do
+ New Project »
+ - else
+ If you will be added to project - it will be displayed here
diff --git a/app/views/dashboard/index.atom.builder b/app/views/dashboard/index.atom.builder
index ffa15258..2bb42a65 100644
--- a/app/views/dashboard/index.atom.builder
+++ b/app/views/dashboard/index.atom.builder
@@ -7,7 +7,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
- if event.allowed?
+ if event.proper?
event = EventDecorator.decorate(event)
xml.entry do
event_link = event.feed_url
diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml
index 6b360dc1..b64aa86c 100644
--- a/app/views/dashboard/index.html.haml
+++ b/app/views/dashboard/index.html.haml
@@ -1,50 +1,11 @@
-- if @projects.any?
+- if @has_authorized_projects
.projects
.activities.span8
- = render "events/event_last_push", event: @last_push
-
- .event_filter
- = event_filter_link EventFilter.push, 'Push events'
- = event_filter_link EventFilter.merged, 'Merge events'
- = event_filter_link EventFilter.comments, 'Comments'
- = event_filter_link EventFilter.team, 'Team'
-
- - if @events.any?
- .content_list= render @events
- - else
- %p.nothing_here_message Projects activity will be displayed here
- .loading.hide
- .side
- - if @groups.present?
- = render "groups", groups: @groups
- = render "projects", projects: @projects
- %div
- %span.rss-icon
- = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
- = image_tag "rss_ui.png", title: "feed"
- %strong News Feed
-
- %hr
- .gitlab-promo
- = link_to "Homepage", "http://gitlabhq.com"
- = link_to "Blog", "http://blog.gitlabhq.com"
- = link_to "@gitlabhq", "https://twitter.com/gitlabhq"
-
+ = render 'activities'
+ .side.span4
+ = render 'sidebar'
- else
- %h3.nothing_here_message There are no projects you have access to.
- %br
- %h4.nothing_here_message
- - if current_user.can_create_project?
- You can create up to
- = current_user.projects_limit
- projects. Click on button below to add a new one
- .link_holder
- = link_to new_project_path, class: "btn primary" do
- New Project »
- - else
- If you will be added to project - it will be displayed here
-
-
+ = render "zero_authorized_projects"
:javascript
$(function(){ Pager.init(20); });
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index e3093bcf..52863229 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -13,8 +13,8 @@
- @issues.group_by(&:project).each do |group|
%div.ui-box
- @project = group[0]
- %h5= link_to(@project.name, project_path(@project))
- %ul.unstyled.issues_table
+ %h5= link_to_project @project
+ %ul.well-list.issues_table
- group[1].each do |issue|
= render(partial: 'issues/show', locals: {issue: issue})
%hr
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 8454cfdc..ea7c8c9a 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -10,11 +10,12 @@
.span9
- if @merge_requests.any?
- @merge_requests.group_by(&:project).each do |group|
- %ul.unstyled.ui-box
+ .ui-box
- @project = group[0]
- %h5= @project.name
- - group[1].each do |merge_request|
- = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request})
+ %h5= link_to_project @project
+ %ul.well-list
+ - group[1].each do |merge_request|
+ = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request})
%hr
= paginate @merge_requests, theme: "gitlab"
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index 38192d71..474e7ef7 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -3,7 +3,7 @@
- else
= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f|
= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo"
- = f.text_field :email, :class => "text top", :placeholder => "Email"
+ = f.email_field :email, :class => "text top", :placeholder => "Email", :autofocus => "autofocus"
= f.password_field :password, :class => "text bottom", :placeholder => "Password"
- if devise_mapping.rememberable?
.clearfix.inputs-list
diff --git a/app/views/errors/gitolite.html.haml b/app/views/errors/gitolite.html.haml
index 2670f2d3..590bca71 100644
--- a/app/views/errors/gitolite.html.haml
+++ b/app/views/errors/gitolite.html.haml
@@ -21,5 +21,5 @@
Permissions:
%pre
= preserve do
- sudo chmod -R 770 #{Gitlab.config.git_base_path}
- sudo chown -R git:git #{Gitlab.config.git_base_path}
+ sudo chown -R git:git #{Gitlab.config.gitolite.repos_path}
+ sudo chmod -R ug+rwXs #{Gitlab.config.gitolite.repos_path}
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 2446b764..191aed07 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -1,15 +1,15 @@
-- if event.allowed?
+- if event.proper?
%div.event-item
- = event_image(event)
+ %span.cgray.right
+ #{time_ago_in_words(event.created_at)} ago.
+
= image_tag gravatar_icon(event.author_email), class: "avatar s24"
- if event.push?
= render "events/event/push", event: event
+ .clearfix
+ - elsif event.note?
+ = render "events/event/note", event: event
- else
= render "events/event/common", event: event
- .clearfix
- %span.cgray.right
- = time_ago_in_words(event.created_at)
- ago.
- .clearfix
diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml
index e15f1ac0..b2376019 100644
--- a/app/views/events/_event_last_push.html.haml
+++ b/app/views/events/_event_last_push.html.haml
@@ -6,7 +6,7 @@
= link_to project_commits_path(event.project, event.ref_name) do
%strong= truncate(event.ref_name, length: 28)
at
- %strong= link_to event.project.name, event.project
+ %strong= link_to_project event.project
%span
= time_ago_in_words(event.created_at)
ago.
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
new file mode 100644
index 00000000..8c129693
--- /dev/null
+++ b/app/views/events/event/_note.html.haml
@@ -0,0 +1,25 @@
+.event-title
+ %span.author_name= link_to_author event
+ %span.event_label commented on #{event.note_target_type}
+ - if event.note_target
+ - if event.note_commit?
+ = link_to event.note_short_commit_id, project_commit_path(event.project, event.note_commit_id), class: "commit_short_id"
+ - else
+ = link_to [event.project, event.note_target] do
+ %strong= truncate event.note_target_id
+
+ - elsif event.wall_note?
+ -# nothing here
+ - else
+ %strong (deleted)
+ at
+ - if event.project
+ = link_to_project event.project
+ - else
+ = event.project_name
+
+.event-body
+ %span.hint
+
+ %i.icon-comment
+ = truncate event.target.note, length: 70
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 869321ed..119b8e82 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -7,12 +7,12 @@
= link_to project_commits_path(event.project, event.ref_name) do
%strong= event.ref_name
at
- %strong= link_to event.project.name, event.project
+ %strong= link_to_project event.project
- if event.push_with_commits?
- project = event.project
.event-body
- %ul.unstyled.event_commits
+ %ul.well-list.event_commits
- few_commits = event.commits[0...2]
- few_commits.each do |commit|
= render "events/commit", commit: commit, project: project
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 39c0b6af..0b491879 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -8,13 +8,13 @@
= link_to new_project_path(namespace_id: @group.id), class: "btn very_small info" do
%i.icon-plus
New Project
- %ul.unstyled
+ %ul.well-list
- if projects.blank?
%p.nothing_here_message This groups has no projects yet
- projects.each do |project|
- %li.wll
+ %li
= link_to project_path(project), class: dom_class(project) do
- %strong.project_name= truncate(project.name, length: 25)
+ %strong.well-title= truncate(project.name, length: 25)
%span.arrow
→
%span.last_activity
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index cc488d57..0daf4d75 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -10,7 +10,7 @@
%div.ui-box
- @project = group[0]
%h5= @project.name
- %ul.unstyled.issues_table
+ %ul.well-list.issues_table
- group[1].each do |issue|
= render(partial: 'issues/show', locals: {issue: issue})
%hr
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 23a7e722..72aa4ad1 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -6,7 +6,7 @@
%br
- if @merge_requests.any?
- @merge_requests.group_by(&:project).each do |group|
- %ul.unstyled.ui-box
+ %ul.well-list.ui-box
- @project = group[0]
%h5= @project.name
- group[1].each do |merge_request|
diff --git a/app/views/groups/people.html.haml b/app/views/groups/people.html.haml
index 68102b6a..be3dd7a4 100644
--- a/app/views/groups/people.html.haml
+++ b/app/views/groups/people.html.haml
@@ -9,9 +9,9 @@
Team
%small
(#{@users.size})
- %ul.unstyled
+ %ul.well-list
- @users.each do |user|
- %li.wll
+ %li
= image_tag gravatar_icon(user.email, 16), class: "avatar s16"
%strong= user.name
%span.cgray= user.email
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index fa3bfade..9aa52ea5 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -7,7 +7,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
- if event.allowed?
+ if event.proper?
event = EventDecorator.decorate(event)
xml.entry do
event_link = event.feed_url
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index b929b267..76bc2639 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -11,7 +11,7 @@
- else
%p.nothing_here_message Projects activity will be displayed here
.loading.hide
- .side
+ .side.span4
= render "projects", projects: @projects
%div
%span.rss-icon
diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml
index f9287fa0..c9ec701a 100644
--- a/app/views/help/permissions.html.haml
+++ b/app/views/help/permissions.html.haml
@@ -4,61 +4,66 @@
← to index
%hr
-.row
- .ui-box.span2
- %h5 Guest
- %ul.unstyled
- %li Create new issue
- %li Leave comments
- %li Write on project wall
+%fieldset
+ %legend Guest
+ %ul
+ %li Create new issue
+ %li Leave comments
+ %li Write on project wall
- .ui-box.span3
- %h5 Reporter
- %ul.unstyled
- %li Create new issue
- %li Leave comments
- %li Write on project wall
- %li Pull project code
- %li Download project
- %li Create new merge request
- %li Create a code snippets
+%fieldset
+ %legend Reporter
+ %ul
+ %li Create new issue
+ %li Leave comments
+ %li Write on project wall
+ %li Pull project code
+ %li Download project
+ %li Create new merge request
+ %li Create a code snippets
- .ui-box.span3
- %h5 Developer
- %ul.unstyled
- %li Create new issue
- %li Leave comments
- %li Write on project wall
- %li Pull project code
- %li Download project
- %li Create new merge request
- %li Create a code snippets
- %li Create new branches
- %li Push to non-protected branches
- %li Remove non-protected branches
- %li Add tags
- %li Write a wiki
+%fieldset
+ %legend Developer
+ %ul
+ %li Create new issue
+ %li Leave comments
+ %li Write on project wall
+ %li Pull project code
+ %li Download project
+ %li Create new merge request
+ %li Create a code snippets
+ %li Create new branches
+ %li Push to non-protected branches
+ %li Remove non-protected branches
+ %li Add tags
+ %li Write a wiki
- .ui-box.span3
- %h5 Master
- %ul.unstyled
- %li Create new issue
- %li Leave comments
- %li Write on project wall
- %li Pull project code
- %li Download project
- %li Create new merge request
- %li Create a code snippets
- %li Create new branches
- %li Push to non-protected branches
- %li Remove non-protected branches
- %li Add tags
- %li Write a wiki
- %li Add new team members
- %li Push to protected branches
- %li Remove protected branches
- %li Push with force option
- %li Edit project
- %li Add Deploy Keys to project
- %li Configure Project Hooks
+%fieldset
+ %legend Master
+ %ul
+ %li Create new issue
+ %li Leave comments
+ %li Write on project wall
+ %li Pull project code
+ %li Download project
+ %li Create new merge request
+ %li Create a code snippets
+ %li Create new branches
+ %li Push to non-protected branches
+ %li Remove non-protected branches
+ %li Add tags
+ %li Write a wiki
+ %li Add new team members
+ %li Push to protected branches
+ %li Remove protected branches
+ %li Push with force option
+ %li Edit project
+ %li Add Deploy Keys to project
+ %li Configure Project Hooks
+
+%fieldset
+ %legend Owner
+ %ul
+ %li Transfer project to another namespace
+ %li Remove project
diff --git a/app/views/hooks/index.html.haml b/app/views/hooks/index.html.haml
index 1b59c8e8..6a36c749 100644
--- a/app/views/hooks/index.html.haml
+++ b/app/views/hooks/index.html.haml
@@ -22,22 +22,21 @@
%hr
-if @hooks.any?
- %h3
- Hooks
- %small (#{@hooks.count})
+ %h3.page_title
+ Hooks (#{@hooks.count})
%br
%table
%thead
%tr
%th URL
- %th Method
%th
- @hooks.each do |hook|
%tr
%td
+ %span.badge.badge-info POST
= link_to project_hook_path(@project, hook) do
%strong= hook.url
- = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn small right"
- %td POST
%td
- = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small right"
+ .right
+ = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn small grouped"
+ = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small grouped"
diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml
index 670b4e05..030f797c 100644
--- a/app/views/issues/_form.html.haml
+++ b/app/views/issues/_form.html.haml
@@ -1,18 +1,18 @@
%div.issue-form-holder
%h3.page_title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}"
- = form_for [@project, @issue], remote: request.xhr? do |f|
+ = form_for [@project, @issue] do |f|
-if @issue.errors.any?
.alert-message.block-message.error
- %ul
- - @issue.errors.full_messages.each do |msg|
- %li= msg
+ - @issue.errors.full_messages.each do |msg|
+ %span= msg
+ %br
.issue_form_box
.issue_title
.clearfix
= f.label :title do
%strong= "Subject *"
.input
- = f.text_field :title, maxlength: 255, class: "xxlarge js-gfm-input", autofocus: true
+ = f.text_field :title, maxlength: 255, class: "xxlarge js-gfm-input", autofocus: true, required: true
.issue_middle_block
.issue_assignee
= f.label :assignee_id do
@@ -47,11 +47,38 @@
-else
= f.submit 'Save changes', class: "save-btn btn"
- - cancel_class = 'btn cancel-btn'
- - if request.xhr?
- = link_to "Cancel", "#back", onclick: "backToIssues();", class: cancel_class
- - else
- - if @issue.new_record?
- = link_to "Cancel", project_issues_path(@project), class: cancel_class
- - else
- = link_to "Cancel", project_issue_path(@project, @issue), class: cancel_class
+ - cancel_path = @issue.new_record? ? project_issues_path(@project) : project_issue_path(@project, @issue)
+ = link_to "Cancel", cancel_path, class: 'btn cancel-btn'
+
+
+
+
+:javascript
+ $(function(){
+ $("#issue_label_list")
+ .bind( "keydown", function( event ) {
+ if ( event.keyCode === $.ui.keyCode.TAB &&
+ $( this ).data( "autocomplete" ).menu.active ) {
+ event.preventDefault();
+ }
+ })
+ .autocomplete({
+ minLength: 0,
+ source: function( request, response ) {
+ response( $.ui.autocomplete.filter(
+ #{raw labels_autocomplete_source}, extractLast( request.term ) ) );
+ },
+ focus: function() {
+ return false;
+ },
+ select: function(event, ui) {
+ var terms = split( this.value );
+ terms.pop();
+ terms.push( ui.item.value );
+ terms.push( "" );
+ this.value = terms.join( ", " );
+ return false;
+ }
+ });
+ });
+
diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml
index f82ae8bd..d7ba4300 100644
--- a/app/views/issues/_issues.html.haml
+++ b/app/views/issues/_issues.html.haml
@@ -6,7 +6,7 @@
.row
.span7= paginate @issues, remote: true, theme: "gitlab"
.span3.right
- %span.cgray.right
+ %span.cgray.right
%span.issue_counter #{@issues.total_count}
issues for this filter
- else
diff --git a/app/views/issues/_show.html.haml b/app/views/issues/_show.html.haml
index 8aa92ebf..4641e8bd 100644
--- a/app/views/issues/_show.html.haml
+++ b/app/views/issues/_show.html.haml
@@ -1,4 +1,4 @@
-%li.wll{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) }
+%li{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) }
- if controller.controller_name == 'issues'
.issue_check
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
@@ -16,7 +16,7 @@
= link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small grouped reopen_issue", remote: true
- else
= link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small grouped close_issue", remote: true
- = link_to edit_project_issue_path(issue.project, issue), class: "btn small edit-issue-link grouped", remote: true do
+ = link_to edit_project_issue_path(issue.project, issue), class: "btn small edit-issue-link grouped" do
%i.icon-edit
Edit
@@ -28,7 +28,7 @@
%p= link_to_gfm truncate(issue.title, length: 100), project_issue_path(issue.project, issue), class: "row_title"
%span.update-author
- %small.cdark= "##{issue.id}"
+ %span.cdark= "##{issue.id}"
- if issue.assignee
assigned to #{issue.assignee_name}
- else
diff --git a/app/views/issues/create.js.haml b/app/views/issues/create.js.haml
deleted file mode 100644
index d90cbf0d..00000000
--- a/app/views/issues/create.js.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-- if @issue.valid?
- :plain
- switchFromNewIssue();
- $("#issues-table").prepend("#{escape_javascript(render(partial: 'show', locals: {issue: @issue}))}");
- $.ajax({type: "GET", url: location.href, dataType: "script"});
-- else
- :plain
- $("#new_issue_dialog").empty();
- $("#new_issue_dialog").append("#{escape_javascript(render('form'))}");
- $('select#issue_assignee_id').chosen();
diff --git a/app/views/issues/edit.js.haml b/app/views/issues/edit.js.haml
deleted file mode 100644
index a994572f..00000000
--- a/app/views/issues/edit.js.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-:plain
- $("#edit_issue_dialog").html("#{escape_javascript(render('form'))}");
- switchToEditIssue();
-
diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml
index d89b183d..08d4393b 100644
--- a/app/views/issues/index.html.haml
+++ b/app/views/issues/index.html.haml
@@ -6,7 +6,7 @@
.right
.span5
- if can? current_user, :write_issue, @project
- = link_to new_project_issue_path(@project), class: "right btn", title: "New Issue", remote: true, id: "new_issue_link" do
+ = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "right btn", title: "New Issue", id: "new_issue_link" do
%i.icon-plus
New Issue
= form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do
@@ -27,7 +27,7 @@
.left
= select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status")
= select_tag('update[assignee_id]', options_from_collection_for_select(@project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee")
- = select_tag('update[milestone_id]', options_from_collection_for_select(@project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone")
+ = select_tag('update[milestone_id]', options_from_collection_for_select(issues_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :f, params[:f]
= button_tag "Save", class: "btn update_selected_issues"
@@ -51,16 +51,13 @@
= form_tag project_issues_path(@project), method: :get, class: :right do
= select_tag(:label_name, options_for_select(issue_tags, params[:label_name]), prompt: "Labels")
= select_tag(:assignee_id, options_from_collection_for_select([unassigned_filter] + @project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee")
- = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone")
+ = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + issues_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone")
= hidden_field_tag :f, params[:f]
.clearfix
- %ul#issues-table.unstyled.issues_table
+ %ul#issues-table.well-list.issues_table
= render "issues"
-#new_issue_dialog
-#edit_issue_dialog
-
:javascript
$(function(){
issuesPage();
diff --git a/app/views/issues/new.js.haml b/app/views/issues/new.js.haml
deleted file mode 100644
index 4cbcc563..00000000
--- a/app/views/issues/new.js.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-:plain
- $("#new_issue_dialog").html("#{escape_javascript(render('form'))}");
- switchToNewIssue();
diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml
index 3d3164fe..b1014edc 100644
--- a/app/views/issues/show.html.haml
+++ b/app/views/issues/show.html.haml
@@ -26,22 +26,16 @@
.main_box
.top_box_content
- %h4
+ %h4.box-title
- if @issue.closed
- .alert-message.error.status_info Closed
- - else
- .alert-message.success.status_info Open
+ .error.status_info Closed
= gfm escape_once(@issue.title)
.middle_box_content
- %cite.cgray Created by
- = image_tag gravatar_icon(@issue.author_email), width: 16, class: "lil_av"
- %strong.author= link_to_issue_author(@issue)
-
- - if @issue.assignee
- %cite.cgray and currently assigned to
- = image_tag gravatar_icon(@issue.assignee_email), width: 16, class: "lil_av"
- %strong.author= link_to_issue_assignee(@issue)
+ %cite.cgray
+ Created by #{link_to_member(@project, @issue.author)}
+ - if @issue.assignee
+ \ and currently assigned to #{link_to_member(@project, @issue.assignee)}
- if @issue.milestone
- milestone = @issue.milestone
diff --git a/app/views/issues/update.js.haml b/app/views/issues/update.js.haml
index 44722895..7f66022a 100644
--- a/app/views/issues/update.js.haml
+++ b/app/views/issues/update.js.haml
@@ -2,13 +2,3 @@
- if @issue.valid?
:plain
$("##{dom_id(@issue)}").fadeOut();
-- else
- - if @issue.valid?
- :plain
- updatePage();
- switchFromEditIssue();
- - else
- :plain
- $("#edit_issue_dialog").empty();
- $("#edit_issue_dialog").append("#{escape_javascript(render('form'))}");
- $('select#issue_assignee_id').chosen();
diff --git a/app/views/kaminari/admin/_gap.html.haml b/app/views/kaminari/admin/_gap.html.haml
index f82f185a..3ffd12f8 100644
--- a/app/views/kaminari/admin/_gap.html.haml
+++ b/app/views/kaminari/admin/_gap.html.haml
@@ -4,5 +4,6 @@
-# num_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-%span.page.gap
- = raw(t 'views.pagination.truncate')
+%li{class: "page"}
+ %span.page.gap
+ = raw(t 'views.pagination.truncate')
diff --git a/app/views/labels/_label.html.haml b/app/views/labels/_label.html.haml
index 8a465a9e..6e223e8e 100644
--- a/app/views/labels/_label.html.haml
+++ b/app/views/labels/_label.html.haml
@@ -1,4 +1,4 @@
-%li.wll
+%li
%strong
%i.icon-tag
= label.name
diff --git a/app/views/labels/index.html.haml b/app/views/labels/index.html.haml
index 4e41d375..6eb2c00e 100644
--- a/app/views/labels/index.html.haml
+++ b/app/views/labels/index.html.haml
@@ -4,7 +4,7 @@
Labels
%br
%div.ui-box
- %ul.unstyled.labels-table
+ %ul.well-list.labels-table
- @labels.each do |label|
= render 'label', label: label
diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml
index 7b2a291d..8f8c7d88 100644
--- a/app/views/layouts/_init_auto_complete.html.haml
+++ b/app/views/layouts/_init_auto_complete.html.haml
@@ -1,6 +1,6 @@
:javascript
$(function() {
- GitLab.GfmAutoComplete.Members.url = "#{ "/api/v2/projects/#{@project.path}/members" if @project }";
+ GitLab.GfmAutoComplete.Members.url = "#{ "/api/v3/projects/#{@project.id}/members" if @project }";
GitLab.GfmAutoComplete.Members.params.private_token = "#{current_user.private_token}";
GitLab.GfmAutoComplete.Emoji.data = #{raw emoji_autocomplete_source};
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 35bf5577..c418e1db 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -3,14 +3,7 @@
%meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
%title
GitLab
- :css
- .header h1 {color: #BBBBBB !important; font: bold 22px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 32px;}
- .header p {color: #c6c6c6; font: normal 12px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 18px;}
- .content h2 {color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; }
- .content p {color:#767676; font-weight: normal; margin: 0; padding: 0; line-height: 20px; font-size: 12px;font-family: Helvetica, Arial, sans-serif;}
- .content a {color: #0eb6ce; text-decoration: none;}
- .footer p {font-size: 11px; color:#7d7a7a; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif;}
- .footer a {color: #0eb6ce; text-decoration: none;}
+
%body{bgcolor: "#EAEAEA", style: "margin: 0; padding: 0; background: #EAEAEA"}
%table{align: "center", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 35px 0; background: #EAEAEA;", width: "100%"}
%tr
@@ -19,11 +12,11 @@
%tr
%td{style: "font-size: 0px;", width: "20"}
\Â
- %td{align: "left", style: "padding: 18px 0 10px;", width: "580"}
- %h1{style: "color: #BBBBBB; font: normal 22px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 32px;"}
+ %td{align: "left", style: "padding: 10px 0", width: "580"}
+ %h1{style: "font-size: 24px; color: #BBBBBB; font: normal 22px Helvetica, Arial, sans-serif; margin: 0; padding: 0; line-height: 32px;"}
GITLAB
- if @project
- | #{@project.name}
+ → #{@project.name_with_namespace}
%table{align: "center", bgcolor: "#fff", border: "0", cellpadding: "0", cellspacing: "0", style: "font-family: Helvetica, Arial, sans-serif; background: #fff;", width: "600"}
%tr= yield
%tr
@@ -35,5 +28,5 @@
%p{style: "font-size: 11px; color:#7d7a7a; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif;"}
You're receiving this notification because you are a member of the
- if @project
- #{@project.name}
+ #{@project.name_with_namespace}
project team.
diff --git a/app/views/layouts/project_resource.html.haml b/app/views/layouts/project_resource.html.haml
index ab8e88c0..70980745 100644
--- a/app/views/layouts/project_resource.html.haml
+++ b/app/views/layouts/project_resource.html.haml
@@ -1,9 +1,9 @@
!!! 5
%html{ lang: "en"}
- = render "layouts/head", title: @project.name
+ = render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} project"}
= render "layouts/flash"
- = render "layouts/head_panel", title: @project.name
+ = render "layouts/head_panel", title: project_title(@project)
- if can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
.container
diff --git a/app/views/merge_requests/_form.html.haml b/app/views/merge_requests/_form.html.haml
index 302e75cf..9606e2e5 100644
--- a/app/views/merge_requests/_form.html.haml
+++ b/app/views/merge_requests/_form.html.haml
@@ -32,7 +32,7 @@
.top_box_content
= f.label :title do
%strong= "Title *"
- .input= f.text_field :title, class: "input-xxlarge pad js-gfm-input", maxlength: 255, rows: 5
+ .input= f.text_field :title, class: "input-xxlarge pad js-gfm-input", maxlength: 255, rows: 5, required: true
.merge_requests_middle_box
.merge_requests_assignee
= f.label :assignee_id do
diff --git a/app/views/merge_requests/_merge_request.html.haml b/app/views/merge_requests/_merge_request.html.haml
index 4f68c5f2..7369f3dd 100644
--- a/app/views/merge_requests/_merge_request.html.haml
+++ b/app/views/merge_requests/_merge_request.html.haml
@@ -1,4 +1,4 @@
-%li.wll{ class: mr_css_classes(merge_request) }
+%li{ class: mr_css_classes(merge_request) }
.right
.left
- if merge_request.merged?
diff --git a/app/views/merge_requests/_show.html.haml b/app/views/merge_requests/_show.html.haml
index 138f6510..8285a56d 100644
--- a/app/views/merge_requests/_show.html.haml
+++ b/app/views/merge_requests/_show.html.haml
@@ -2,6 +2,8 @@
= render "merge_requests/show/how_to_merge"
= render "merge_requests/show/mr_box"
= render "merge_requests/show/mr_accept"
+- if @project.gitlab_ci?
+ = render "merge_requests/show/mr_ci"
= render "merge_requests/show/commits"
- if @commits.present?
@@ -26,6 +28,8 @@
MergeRequest.init({
url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
check_enable: #{@merge_request.state == MergeRequest::UNCHECKED ? "true" : "false"},
+ url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
+ ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
current_state: "#{@merge_request.human_state}",
action: "#{controller.action_name}"
});
diff --git a/app/views/merge_requests/index.html.haml b/app/views/merge_requests/index.html.haml
index 7bcb7a81..5b234bfb 100644
--- a/app/views/merge_requests/index.html.haml
+++ b/app/views/merge_requests/index.html.haml
@@ -30,7 +30,7 @@
= hidden_field_tag :f, params[:f]
.clearfix
- %ul.unstyled
+ %ul.well-list
= render @merge_requests
- if @merge_requests.blank?
%li
diff --git a/app/views/merge_requests/show/_commits.html.haml b/app/views/merge_requests/show/_commits.html.haml
index d25e707c..79692277 100644
--- a/app/views/merge_requests/show/_commits.html.haml
+++ b/app/views/merge_requests/show/_commits.html.haml
@@ -5,19 +5,19 @@
Commits (#{@commits.count})
.merge-request-commits
- if @commits.count > 8
- %ul.first_mr_commits.unstyled
+ %ul.first_mr_commits.well-list
- @commits.first(8).each do |commit|
= render "commits/commit", commit: commit
%li.bottom
8 of #{@commits.count} commits displayed.
%strong
%a.mr_show_all_commits Click here to show all
- %ul.all_mr_commits.hide.unstyled
+ %ul.all_mr_commits.hide.well-list
- @commits.each do |commit|
= render "commits/commit", commit: commit
- else
- %ul.unstyled
+ %ul.well-list
- @commits.each do |commit|
= render "commits/commit", commit: commit
diff --git a/app/views/merge_requests/show/_mr_box.html.haml b/app/views/merge_requests/show/_mr_box.html.haml
index b4b4be29..cd33732d 100644
--- a/app/views/merge_requests/show/_mr_box.html.haml
+++ b/app/views/merge_requests/show/_mr_box.html.haml
@@ -1,25 +1,20 @@
.main_box
.top_box_content
- %h4
- - if @merge_request.closed
- .alert-message.error.status_info Closed
- - else
- .alert-message.success.status_info Open
+ %h4.box-title
+ - if @merge_request.merged
+ .error.status_info
+ %i.icon-ok
+ Merged
+ - elsif @merge_request.closed
+ .error.status_info Closed
= gfm escape_once(@merge_request.title)
- - if @project.gitlab_ci?
- .right
- = image_tag ci_status_path, class: 'status-badge'
.middle_box_content
%div
- %cite.cgray Created at #{@merge_request.created_at.stamp("Aug 21, 2011")} by
- = image_tag gravatar_icon(@merge_request.author_email), width: 16, class: "lil_av"
- %strong.author= link_to_merge_request_author(@merge_request)
-
- - if @merge_request.assignee
- %cite.cgray , currently assigned to
- = image_tag gravatar_icon(@merge_request.assignee_email), width: 16, class: "lil_av"
- %strong.author= link_to_merge_request_assignee(@merge_request)
+ %cite.cgray
+ Created at #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)}
+ - if @merge_request.assignee
+ \, currently assigned to #{link_to_member(@project, @merge_request.assignee)}
- if @merge_request.milestone
- milestone = @merge_request.milestone
%cite.cgray and attached to milestone
@@ -30,10 +25,10 @@
.bottom_box_content
- if @merge_request.merged?
%span
- Merged by #{@merge_request.merge_event.author_name}
+ Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
%small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago.
- elsif @merge_request.closed_event
%span
- Closed by #{@merge_request.closed_event.author_name}
+ Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
%small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
diff --git a/app/views/merge_requests/show/_mr_ci.html.haml b/app/views/merge_requests/show/_mr_ci.html.haml
new file mode 100644
index 00000000..d46b606e
--- /dev/null
+++ b/app/views/merge_requests/show/_mr_ci.html.haml
@@ -0,0 +1,35 @@
+- if @merge_request.open? && @commits.any?
+ .ci_widget.ci-success{style: "display:none"}
+ .alert.alert-success
+ %i.icon-ok
+ %strong CI build passed
+ for #{@merge_request.last_commit_short_sha}.
+ = link_to "Build page", ci_build_details_path(@merge_request)
+
+
+ .ci_widget.ci-failed{style: "display:none"}
+ .alert.alert-error
+ %i.icon-remove
+ %strong CI build failed
+ for #{@merge_request.last_commit_short_sha}.
+ = link_to "Build page", ci_build_details_path(@merge_request)
+
+ - [:running, :pending].each do |status|
+ .ci_widget{class: "ci-#{status}", style: "display:none"}
+ .alert
+ %i.icon-time
+ %strong CI build #{status}
+ for #{@merge_request.last_commit_short_sha}.
+ = link_to "Build page", ci_build_details_path(@merge_request)
+
+ .ci_widget
+ .alert-message
+ %strong
+ %i.icon-refresh
+ Checking for CI status for #{@merge_request.last_commit_short_sha}
+
+ .ci_widget.ci-error{style: "display:none"}
+ .alert.alert-error
+ %i.icon-remove
+ %strong Cannot connect to CI server. Please check your setting
+
diff --git a/app/views/merge_requests/show/_mr_title.html.haml b/app/views/merge_requests/show/_mr_title.html.haml
index a5275650..c2ffe8e3 100644
--- a/app/views/merge_requests/show/_mr_title.html.haml
+++ b/app/views/merge_requests/show/_mr_title.html.haml
@@ -6,11 +6,6 @@
%span.label_branch= @merge_request.target_branch
%span.right
- - if @merge_request.merged?
- %span.btn.small.disabled.grouped
- %strong
- %i.icon-ok
- = "MERGED"
- if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open?
.left.btn-group
diff --git a/app/views/milestones/_milestone.html.haml b/app/views/milestones/_milestone.html.haml
index 7c4c0e67..3864792f 100644
--- a/app/views/milestones/_milestone.html.haml
+++ b/app/views/milestones/_milestone.html.haml
@@ -1,22 +1,27 @@
-%li{class: "milestone", id: dom_id(milestone) }
+%li{class: "milestone milestone-#{milestone.closed ? 'closed' : 'open'}", id: dom_id(milestone) }
.right
- - if can? current_user, :admin_milestone, milestone.project
+ - if can?(current_user, :admin_milestone, milestone.project) and milestone.open?
= link_to edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped" do
%i.icon-edit
Edit
%h4
= link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone)
+ - if milestone.expired? and not milestone.closed
+ %span.cred (Expired)
%small
= milestone.expires_at
- .row
- .span4
- .progress.progress-info
- .bar{style: "width: #{milestone.percent_complete}%;"}
- .span6
- = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do
- = pluralize milestone.issues.count, 'Issue'
-
- = link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do
- = pluralize milestone.merge_requests.count, 'Merge Request'
-
- %span.light #{milestone.percent_complete}% complete
+ - if milestone.is_empty?
+ %span.muted Empty
+ - else
+ .row
+ .span4
+ .progress.progress-info
+ .bar{style: "width: #{milestone.percent_complete}%;"}
+ .span6
+ = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do
+ = pluralize milestone.issues.count, 'Issue'
+
+ = link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do
+ = pluralize milestone.merge_requests.count, 'Merge Request'
+
+ %span.light #{milestone.percent_complete}% complete
diff --git a/app/views/milestones/index.html.haml b/app/views/milestones/index.html.haml
index c5333b08..3089595f 100644
--- a/app/views/milestones/index.html.haml
+++ b/app/views/milestones/index.html.haml
@@ -11,15 +11,18 @@
%li{class: ("active" if (params[:f] == "active" || !params[:f]))}
= link_to project_milestones_path(@project, f: "active") do
Active
+ %li{class: ("active" if params[:f] == "closed")}
+ = link_to project_milestones_path(@project, f: "closed") do
+ Closed
%li{class: ("active" if params[:f] == "all")}
= link_to project_milestones_path(@project, f: "all") do
All
- %ul.unstyled
+ %ul.well-list
= render @milestones
- if @milestones.present?
- %li.bottom= paginate @milestones, remote: true, theme: "gitlab"
+ %li.bottom= paginate @milestones, theme: "gitlab"
- else
%li
%h3.nothing_here_message Nothing to show here
diff --git a/app/views/milestones/show.html.haml b/app/views/milestones/show.html.haml
index b8bc788c..c4975c72 100644
--- a/app/views/milestones/show.html.haml
+++ b/app/views/milestones/show.html.haml
@@ -1,31 +1,41 @@
-%h3.page_title
- Milestone ##{@milestone.id}
- %small
- = @milestone.expires_at
+.row
+ .span6
+ %h3.page_title
+ Milestone ##{@milestone.id}
+ %small
+ = @milestone.expires_at
+ .back_link
+ = link_to project_milestones_path(@project) do
+ ← To milestones list
+ .span6
+ .right
+ - unless @milestone.closed
+ = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn small grouped", title: "New Issue" do
+ %i.icon-plus
+ New Issue
+ = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link small grouped"
+ - if can?(current_user, :admin_milestone, @project)
+ = link_to edit_project_milestone_path(@project, @milestone), class: "btn small grouped" do
+ %i.icon-edit
+ Edit
- %span.right
- = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn small grouped", title: "New Issue" do
- %i.icon-plus
- New Issue
- = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link small grouped"
- - if can?(current_user, :admin_milestone, @project)
- = link_to edit_project_milestone_path(@project, @milestone), class: "btn small grouped" do
- %i.icon-edit
- Edit
-.back_link
- = link_to project_milestones_path(@project) do
- ← To milestones list
+
+- if @milestone.can_be_closed?
+ %hr
+ %p
+ %span All issues for this milestone are closed. You may close milestone now.
+ = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {closed: true }), method: :put, class: "btn small danger"
.main_box
.top_box_content
- %h5
+ %h4.box-title
- if @milestone.closed
- .alert-message.error.status_info Closed
- - else
- .alert-message.success.status_info Open
+ .error.status_info Closed
+ - elsif @milestone.expired?
+ .error.status_info Expired
+
= gfm escape_once(@milestone.title)
- %small.right= @milestone.expires_at
.middle_box_content
%h5
@@ -34,6 +44,7 @@
#{@milestone.closed_items_count} closed
–
#{@milestone.open_items_count} open
+ %span.right= @milestone.expires_at
.progress.progress-info
.bar{style: "width: #{@milestone.percent_complete}%;"}
@@ -43,14 +54,16 @@
= preserve do
= markdown @milestone.description
+
.row
.span6
%table.milestone-issue-filter
%thead
- %th
- %ul.nav.nav-pills
- %li.active= link_to('Open Issues', '#')
- %li=link_to('All Issues', '#')
+ %tr
+ %th
+ %ul.nav.nav-pills
+ %li.active= link_to('Open Issues', '#')
+ %li=link_to('All Issues', '#')
- @issues.each do |issue|
%tr{data: {closed: issue.closed}}
%td
@@ -62,10 +75,11 @@
.span6
%table.milestone-merge-requests-filter
%thead
- %th
- %ul.nav.nav-pills
- %li.active= link_to('Open Merge Requests', '#')
- %li=link_to('All Merge Requests', '#')
+ %tr
+ %th
+ %ul.nav.nav-pills
+ %li.active= link_to('Open Merge Requests', '#')
+ %li=link_to('All Merge Requests', '#')
- @merge_requests.each do |merge_request|
%tr{data: {closed: merge_request.closed}}
%td
diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml
index 57811daf..c310fac4 100644
--- a/app/views/notes/_form.html.haml
+++ b/app/views/notes/_form.html.haml
@@ -1,6 +1,7 @@
= form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note js-new-note-form" } do |f|
= note_target_fields
+ = f.hidden_field :commit_id
= f.hidden_field :line_code
= f.hidden_field :noteable_id
= f.hidden_field :noteable_type
diff --git a/app/views/notify/issue_status_changed_email.html.haml b/app/views/notify/issue_status_changed_email.html.haml
index 59130f79..c433e80c 100644
--- a/app/views/notify/issue_status_changed_email.html.haml
+++ b/app/views/notify/issue_status_changed_email.html.haml
@@ -9,7 +9,7 @@
%tr
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%td{align: "left", style: "padding: 20px 0 0;"}
- %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
+ %p{style: "color:#646464 !important; line-height: 26px; font-size: 16px; font-family: Helvetica, Arial, sans-serif; "}
= "Issue ##{@issue.id}"
= link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title
%br
diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml
index 654d6cd1..fba4b865 100644
--- a/app/views/notify/new_issue_email.html.haml
+++ b/app/views/notify/new_issue_email.html.haml
@@ -9,7 +9,7 @@
%tr
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%td{align: "left", style: "padding: 20px 0 0;"}
- %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
+ %p{style: "color:#646464 !important; line-height: 26px; font-size: 16px; font-family: Helvetica, Arial, sans-serif; "}
= "Issue ##{@issue.id}"
= link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title
%br
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index 151aac45..98197670 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -5,7 +5,8 @@
%td{align: "left", style: "padding: 20px 0 0;"}
%h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
= "New Merge Request !#{@merge_request.id}"
- = link_to_gfm truncate(@merge_request.title, length: 16), project_merge_request_url(@merge_request.project, @merge_request)
+ %p{style: "color:#646464 !important; line-height: 26px; font-size: 16px; font-family: Helvetica, Arial, sans-serif; "}
+ = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.project, @merge_request)
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%tr
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
diff --git a/app/views/notify/project_access_granted_email.html.haml b/app/views/notify/project_access_granted_email.html.haml
index 72b3f065..11117bf0 100644
--- a/app/views/notify/project_access_granted_email.html.haml
+++ b/app/views/notify/project_access_granted_email.html.haml
@@ -1,14 +1,15 @@
%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"}
%table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #717171; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"}
%tr
- %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
- %td{align: "left", style: "padding: 20px 0 0;"}
- %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
+ %td{width: "21"}
+ %td
+ %h2{style: "color:#646464;" }
= "You have been granted #{@users_project.project_access_human} access to project"
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%tr
- %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
- %td{align: "left", style: "padding: 20px 0 0;"}
- %h2{style: "color:#646464 !important; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
- = link_to_gfm truncate(@project.name, length: 45), project_url(@project), title: @project.name
+ %td{width: "21"}
+ %td
+ %h3
+ = link_to project_url(@project) do
+ = @project.name_with_namespace
%br
diff --git a/app/views/notify/project_was_moved_email.html.haml b/app/views/notify/project_was_moved_email.html.haml
new file mode 100644
index 00000000..222bd0fe
--- /dev/null
+++ b/app/views/notify/project_was_moved_email.html.haml
@@ -0,0 +1,25 @@
+%td.content{align: "left", style: "font-family: Helvetica, Arial, sans-serif; padding: 20px 0 0;", valign: "top", width: "600"}
+ %table{border: "0", cellpadding: "0", cellspacing: "0", style: "color: #555; font: normal 11px Helvetica, Arial, sans-serif; margin: 0; padding: 0;", width: "600"}
+ %tr
+ %td{width: "21"}
+ %td
+ %h2
+ = "Project was moved to another location"
+ %td{width: "21"}
+ %tr
+ %td{width: "21"}
+ %td
+ %p
+ The project is now located under
+ = link_to project_url(@project) do
+ = @project.name_with_namespace
+ %p
+ To update the remote url in your local repository run:
+ %br
+ %table{border: "0", cellpadding: "0", cellspacing: "0", width: "558"}
+ %tr
+ %td{valign: "top"}
+ %p{ style: "background:#f5f5f5; padding:10px; border:1px solid #ddd" }
+ git remote set-url origin #{@project.ssh_url_to_repo}
+ %br
+ %td{ width: "21"}
diff --git a/app/views/notify/reassigned_issue_email.html.haml b/app/views/notify/reassigned_issue_email.html.haml
index c7896af3..31a5d232 100644
--- a/app/views/notify/reassigned_issue_email.html.haml
+++ b/app/views/notify/reassigned_issue_email.html.haml
@@ -5,7 +5,7 @@
%td{align: "left", style: "padding: 20px 0 0;"}
%h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
= "Reassigned Issue ##{@issue.id}"
- = link_to_gfm truncate(@issue.title, length: 16), project_issue_url(@issue.project, @issue)
+ = link_to_gfm truncate(@issue.title, length: 30), project_issue_url(@issue.project, @issue)
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%tr
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
diff --git a/app/views/notify/reassigned_merge_request_email.html.haml b/app/views/notify/reassigned_merge_request_email.html.haml
index e49b7836..8f7308b3 100644
--- a/app/views/notify/reassigned_merge_request_email.html.haml
+++ b/app/views/notify/reassigned_merge_request_email.html.haml
@@ -5,7 +5,7 @@
%td{align: "left", style: "padding: 20px 0 0;"}
%h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
= "Reassigned Merge Request !#{@merge_request.id}"
- = link_to_gfm truncate(@merge_request.title, length: 16), project_merge_request_url(@merge_request.project, @merge_request)
+ = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.project, @merge_request)
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%tr
%td{style: "font-size: 1px; line-height: 1px;", width: "21"}
diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml
index 1c51f48f..3c290948 100644
--- a/app/views/profiles/account.html.haml
+++ b/app/views/profiles/account.html.haml
@@ -1,4 +1,4 @@
-- if Gitlab.config.omniauth_enabled?
+- if Gitlab.config.omniauth.enabled
%fieldset
%legend Social Accounts
.oauth_select_holder
@@ -71,6 +71,9 @@
%span.update-failed.cred.hide
%i.icon-ok
Failed
+ %ul.cred
+ %li It will change web url for personal projects.
+ %li It will change the git path to repositories for personal projects.
.input
= f.submit 'Save username', class: "btn save-btn"
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index ac36fa3a..934c1fdf 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -33,11 +33,11 @@
%ul
%li
%p You can change your password on Account page
- -unless Gitlab.config.disable_gravatar?
+ - if Gitlab.config.gravatar.enabled
%li
%p You can change your avatar at #{link_to "gravatar.com", "http://gravatar.com"}
- - if Gitlab.config.omniauth_enabled? && @user.provider?
+ - if Gitlab.config.omniauth.enabled && @user.provider?
%li
%p
You can login through #{@user.provider.titleize}!
diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml
index 9bb411ad..7044d1f2 100644
--- a/app/views/projects/_form.html.haml
+++ b/app/views/projects/_form.html.haml
@@ -17,13 +17,6 @@
.controls
= text_field_tag :ppath, @project.path_to_repo, class: "xxlarge", readonly: true
- .control-group
- = f.label :namespace_id do
- %span Namespace
- .controls
- = f.select :namespace_id, namespaces_options(@project.namespace_id), {prompt: 'Choose a project namespace'}, {class: 'chosen'}
-
- %span.cred Be careful. Changing project namespace can have unintended side effects
- unless @project.heads.empty?
.clearfix
@@ -57,11 +50,28 @@
= f.check_box :wiki_enabled
%span.descr Pages for project documentation
+
+ - if can? current_user, :change_namespace, @project
+ %fieldset.features
+ %legend Transfer:
+ .control-group
+ = f.label :namespace_id do
+ %span Namespace
+ .controls
+ = f.select :namespace_id, namespaces_options(@project.namespace_id || Namespace::global_id), {prompt: 'Choose a project namespace'}, {class: 'chosen'}
+ %br
+ %ul.prepend-top-10.cred
+ %li Be careful. Changing project namespace can have unintended side effects
+ %li You can transfer project only to namespaces you can manage
+ %li You will need to update your local repositories to point to the new location.
+
+
%br
.actions
= f.submit 'Save', class: "btn save-btn"
= link_to 'Cancel', @project, class: "btn"
- unless @project.new_record?
- .right
- = link_to 'Remove', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger"
+ - if can?(current_user, :remove_project, @project)
+ .right
+ = link_to 'Remove', @project, confirm: 'Removed project can not be restored! Are you sure?', method: :delete, class: "btn danger"
diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml
index ce73fe0c..d3889886 100644
--- a/app/views/projects/create.js.haml
+++ b/app/views/projects/create.js.haml
@@ -9,3 +9,4 @@
$('.project_new_holder').show();
$("#new_project").replaceWith("#{escape_javascript(render('new_form'))}");
$('.save-project-loader').hide();
+ new Projects();
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index f331ae7f..52dff687 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -29,6 +29,6 @@
git remote add origin #{@project.url_to_repo}
git push -u origin master
- - if can? current_user, :admin_project, @project
+ - if can? current_user, :remove_project, @project
.prepend-top-20
= link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right"
diff --git a/app/views/projects/files.html.haml b/app/views/projects/files.html.haml
index 9f7efcdc..d1083083 100644
--- a/app/views/projects/files.html.haml
+++ b/app/views/projects/files.html.haml
@@ -17,7 +17,6 @@
= time_ago_in_words(note.created_at)
ago
- else
- .alert-message.block-message
- %span All files attached to project wall, issues etc will be displayed here
+ %p.slead All files attached to project wall, issues etc will be displayed here
diff --git a/app/views/projects/graph.html.haml b/app/views/projects/graph.html.haml
index 07f038d2..4e0b0e36 100644
--- a/app/views/projects/graph.html.haml
+++ b/app/views/projects/graph.html.haml
@@ -2,13 +2,15 @@
%br
.graph_holder
%h4
- %small You can move around the graph by using arrow keys.
+ %small You can move around the graph by using the arrow keys.
#holder.graph
+ .loading.loading-gray
+
:javascript
- var chunk1={commits:#{@commits_json}};
- var days=#{@days_json};
- initGraph();
+ var branch_graph;
$(function(){
- branchGraph($("#holder")[0]);
- GraphNav.init();
+ branch_graph = new BranchGraph($("#holder"), {
+ url: '#{url_for controller: 'projects', action: 'graph', format: :json}',
+ commit_url: '#{url_for controller: 'projects', action: 'show'}/commits/%s'
+ });
});
diff --git a/app/views/projects/update_failed.js.haml b/app/views/projects/update_failed.js.haml
new file mode 100644
index 00000000..a3ac5f40
--- /dev/null
+++ b/app/views/projects/update_failed.js.haml
@@ -0,0 +1,2 @@
+:plain
+ $(".save-project-loader").replaceWith(errorMessage('#{escape_javascript(@error.message)}'));
diff --git a/app/views/services/_gitlab_ci.html.haml b/app/views/services/_gitlab_ci.html.haml
index 4c1ec5bc..649c5cc4 100644
--- a/app/views/services/_gitlab_ci.html.haml
+++ b/app/views/services/_gitlab_ci.html.haml
@@ -1,16 +1,19 @@
%h3.page_title
- Services → GitLab CI Integration
-
+ GitLab CI
+ %small Continuous integration server from GitLab
.right
- .thumbnail
- - if @service.active
- = image_tag 'service-gitlab-ci.png', class: 'small'
- - else
- = image_tag 'service-disabled-gitlab-ci.png', class: 'small'
+ - if @service.active
+ %small.cgreen Enabled
+ - else
+ %small.cgray Disabled
+
+
+
+.back_link
+ = link_to project_services_path(@project) do
+ ← to services
%hr
-
-
= form_for(@service, :as => :service, :url => project_service_path(@project, :gitlab_ci), :method => :put) do |f|
- if @service.errors.any?
.alert-message.block-message.error
diff --git a/app/views/services/index.html.haml b/app/views/services/index.html.haml
index 3894fcee..2c94f965 100644
--- a/app/views/services/index.html.haml
+++ b/app/views/services/index.html.haml
@@ -1,15 +1,31 @@
= render "projects/project_head"
%h3.page_title Services
-%hr
-
-.row
- .span6
- .padded
- %p.slead Continuous integration server from GitLab
- .thumbnail.left
- = link_to edit_project_service_path(@project, :gitlab_ci) do
- - if @gitlab_ci_service.try :active
- = image_tag 'service-gitlab-ci.png'
- - else
- = image_tag 'service-disabled-gitlab-ci.png'
+%br
+%ul.ui-box.well-list
+ %li
+ %h4.cgreen
+ = link_to edit_project_service_path(@project, :gitlab_ci) do
+ GitLab CI
+ %small Continuous integration server from GitLab
+ .right
+ - if @gitlab_ci_service.try(:active)
+ %small.cgreen
+ %i.icon-ok
+ Enabled
+ - else
+ %small.cgray
+ %i.icon-off
+ Disabled
+ %li.disabled
+ %h4
+ Jenkins CI
+ %small An extendable open source continuous integration server
+ .right
+ %small Not implemented yet
+ %li.disabled
+ %h4
+ Campfire
+ %small Web-based group chat tool
+ .right
+ %small Not implemented yet
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index f632e122..e283d9b3 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,4 +1,4 @@
.input-prepend.project_clone_holder
%button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH
- %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.web_protocol.upcase
+ %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge"
diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml
index 981c7cf0..baef737b 100644
--- a/app/views/snippets/_form.html.haml
+++ b/app/views/snippets/_form.html.haml
@@ -1,28 +1,41 @@
%h3.page_title
= @snippet.new_record? ? "New Snippet" : "Edit Snippet ##{@snippet.id}"
%hr
-= form_for [@project, @snippet] do |f|
- -if @snippet.errors.any?
- .alert-message.block-message.error
- %ul
- - @snippet.errors.full_messages.each do |msg|
- %li= msg
+.snippet-form-holder
+ = form_for [@project, @snippet] do |f|
+ -if @snippet.errors.any?
+ .alert-message.block-message.error
+ %ul
+ - @snippet.errors.full_messages.each do |msg|
+ %li= msg
- .clearfix
- = f.label :title
- .input= f.text_field :title, placeholder: "Example Snippet"
- .clearfix
- = f.label :file_name
- .input= f.text_field :file_name, placeholder: "example.rb"
- .clearfix
- = f.label "Lifetime"
- .input= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'}
- .clearfix
- = f.label :content, "Code"
- .input= f.text_area :content, class: "span8"
+ .clearfix
+ = f.label :title
+ .input= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
+ .clearfix
+ = f.label "Lifetime"
+ .input= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'}
+ .clearfix
+ .file-editor
+ = f.label :file_name, "File"
+ .input
+ .file_holder.snippet
+ .file_title
+ = f.text_field :file_name, placeholder: "example.rb", class: 'snippet-file-name', required: true
+ .file_content.code
+ %pre#editor= @snippet.content
+ = f.hidden_field :content, class: 'snippet-file-content'
+
+ .form-actions
+ = f.submit 'Save', class: "save-btn btn"
+ = link_to "Cancel", project_snippets_path(@project), class: " btn"
+ - unless @snippet.new_record?
+ .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
+
+
+:javascript
+ var editor = ace.edit("editor");
+ $(".snippet-form-holder form").submit(function(){
+ $(".snippet-file-content").val(editor.getValue());
+ });
- .form-actions
- = f.submit 'Save', class: "primary btn"
- = link_to "Cancel", project_snippets_path(@project), class: " btn"
- - unless @snippet.new_record?
- .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml
index a2d3a65e..a576500c 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/snippets/_snippet.html.haml
@@ -1,12 +1,13 @@
%tr
%td
+ = image_tag gravatar_icon(snippet.author_email), class: "avatar s24"
%a{href: project_snippet_path(snippet.project, snippet)}
%strong= truncate(snippet.title, length: 60)
%td
= snippet.file_name
%td
%span.cgray
- - if snippet.expires_at
+ - if snippet.expires_at
= snippet.expires_at.to_date.to_s(:short)
- else
Never
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index 515daec6..7b8f94de 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -1,21 +1,21 @@
= render "projects/project_head"
-- if can? current_user, :write_snippet, @project
- .alert-message.block-message
+%h3.page_title
+ Snippets
+ %small share code pastes with others out of git repository
+
+ - if can? current_user, :write_snippet, @project
= link_to new_project_snippet_path(@project), class: "btn small add_new right", title: "New Snippet" do
Add new snippet
- Share code pastes with others if it can't be in a git repository
- %br
- To add new snippet - click on button.
-
+%br
%table
%thead
%tr
%th Title
%th File Name
%th Expires At
- = render @snippets.fresh
- - if @snippets.fresh.empty?
+ = render @snippets
+ - if @snippets.empty?
%tr
%td{colspan: 3}
%h3.nothing_here_message Nothing here.
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 5b9d3d5d..02022185 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,6 +1,6 @@
= render "projects/project_head"
-%h3
+%h3.page_title
= @snippet.title
%small= @snippet.file_name
- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
diff --git a/app/views/team_members/_form.html.haml b/app/views/team_members/_form.html.haml
index 92167138..e5d9a4a4 100644
--- a/app/views/team_members/_form.html.haml
+++ b/app/views/team_members/_form.html.haml
@@ -11,7 +11,7 @@
%h6 1. Choose people you want in the team
.clearfix
= f.label :user_ids, "People"
- .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true})
+ .input= select_tag(:user_ids, options_from_collection_for_select(User.active.not_in_project(@project).all, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true})
%h6 2. Set access level for them
.clearfix
diff --git a/app/views/team_members/_show.html.haml b/app/views/team_members/_show.html.haml
index 8938c7d8..8082f47f 100644
--- a/app/views/team_members/_show.html.haml
+++ b/app/views/team_members/_show.html.haml
@@ -1,6 +1,6 @@
- user = member.user
- allow_admin = can? current_user, :admin_project, @project
-%li.wll{id: dom_id(member), class: "team_member_row user_#{user.id}"}
+%li{id: dom_id(member), class: "team_member_row user_#{user.id}"}
.row
.span6
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
diff --git a/app/views/team_members/_team.html.haml b/app/views/team_members/_team.html.haml
index 65f17864..462e75af 100644
--- a/app/views/team_members/_team.html.haml
+++ b/app/views/team_members/_team.html.haml
@@ -1,10 +1,10 @@
- grouper_project_members(@project).each do |access, members|
- %fieldset
- %legend
+ .ui-box
+ %h5
= Project.access_options.key(access).pluralize
%small= members.size
- %ul.unstyled
- - members.each do |up|
+ %ul.well-list
+ - members.sort_by(&:user_name).each do |up|
= render(partial: 'team_members/show', locals: {member: up})
diff --git a/app/views/team_members/show.html.haml b/app/views/team_members/show.html.haml
index 9d03cd2c..af9a6e6b 100644
--- a/app/views/team_members/show.html.haml
+++ b/app/views/team_members/show.html.haml
@@ -6,7 +6,7 @@
= 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
= image_tag gravatar_icon(user.email, 60), class: "borders"
- %h3
+ %h3.page_title
= user.name
%small
= user.email
diff --git a/app/views/tree/_tree.html.haml b/app/views/tree/_tree.html.haml
index 02ae3d90..a632bb3b 100644
--- a/app/views/tree/_tree.html.haml
+++ b/app/views/tree/_tree.html.haml
@@ -16,10 +16,11 @@
- else
%table#tree-slider{class: "table_#{@hex_path} tree-table" }
%thead
- %th Name
- %th Last Update
- %th Last Commit
- %th= link_to "history", project_commits_path(@project, @id), class: "btn very_small right"
+ %tr
+ %th Name
+ %th Last Update
+ %th Last Commit
+ %th= link_to "history", project_commits_path(@project, @id), class: "btn very_small right"
- if tree.up_dir?
%tr.tree-item
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 4f4f69c4..1414ed49 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -1,12 +1,16 @@
class PostReceive
@queue = :post_receive
- def self.perform(reponame, oldrev, newrev, ref, identifier)
- project = Project.find_by_path(reponame)
+ def self.perform(repo_path, oldrev, newrev, ref, identifier)
+ repo_path.gsub!(Gitlab.config.gitolite.repos_path.to_s, "")
+ repo_path.gsub!(/.git$/, "")
+ repo_path.gsub!(/^\//, "")
+
+ project = Project.find_with_namespace(repo_path)
return false if project.nil?
# Ignore push from non-gitlab users
- user = if identifier.eql? Gitlab.config.gitolite_admin_key
+ user = if identifier.eql? Gitlab.config.gitolite.admin_key
email = project.commit(newrev).author.email rescue nil
User.find_by_email(email) if email
elsif /^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier)
diff --git a/config/database.yml.example b/config/database.yml.example
deleted file mode 100644
index c5a2b8d6..00000000
--- a/config/database.yml.example
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# PRODUCTION
-#
-production:
- adapter: mysql2
- encoding: utf8
- reconnect: false
- database: gitlabhq_production
- pool: 5
- username: root
- password: "secure password"
- # host: localhost
- # socket: /tmp/mysql.sock
-
-#
-# Development specific
-#
-development:
- adapter: mysql2
- encoding: utf8
- reconnect: false
- database: gitlabhq_development
- pool: 5
- username: root
- password: "secure password"
- # socket: /tmp/mysql.sock
-
-# Warning: The database defined as "test" will be erased and
-# re-generated from your development database when you run "rake".
-# Do not set this db to the same as development or production.
-test: &test
- adapter: mysql2
- encoding: utf8
- reconnect: false
- database: gitlabhq_test
- pool: 5
- username: root
- password: "secure password"
- # socket: /tmp/mysql.sock
diff --git a/config/database.yml.postgresql b/config/database.yml.postgresql
index 17b38f3d..0e873d2b 100644
--- a/config/database.yml.postgresql
+++ b/config/database.yml.postgresql
@@ -9,6 +9,7 @@ production:
username: postgres
password:
# host: localhost
+ # port: 5432
# socket: /tmp/postgresql.sock
#
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 35683489..067dbd9b 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -1,55 +1,65 @@
-# # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # #
# Gitlab application config file #
# # # # # # # # # # # # # # # # # #
#
-# 1. Common settings
+# 1. GitLab app settings
# ==========================
-# Web application specific settings
-web:
+## GitLab settings
+gitlab:
+ ## Web server settings
host: localhost
port: 80
https: false
-# Email used for notification
-# about new issues, comments
-email:
- from: notify@localhost
+ ## Email settings
+ # Email address used in the "From" field in mails sent by GitLab
+ email_from: gitlab@localhost
-# Application specific settings
-# Like default project limit for user etc
-app:
+ ## Project settings
default_projects_limit: 10
- # backup_path: "/vol/backups" # default: Rails.root + backups/
- # backup_keep_time: 604800 # default: 0 (forever) (in seconds)
- # disable_gravatar: true # default: false - Disable user avatars from Gravatar.com
+
+## Gravatar
+gravatar:
+ enabled: true # Use user avatar images from Gravatar.com (default: true)
+ # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
+ # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
+
#
# 2. Auth settings
# ==========================
-ldap:
+
+## LDAP settings
+ldap:
enabled: false
host: '_your_ldap_server'
base: '_the_base_where_you_search_for_users'
port: 636
uid: 'sAMAccountName'
- method: 'ssl' # plain
+ method: 'ssl' # "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
+## Omniauth settings
omniauth:
# Enable ability for users
- # to login via twitter, google ..
+ # Allow logging in via Twitter, Google, etc. using Omniauth providers
enabled: false
- # IMPORTANT!
- # It allows user to login without having user account
+ # CAUTION!
+ # This allows users to login without having a user account first (default: false)
+ # User accounts will be created automatically when authentication was successful.
allow_single_sign_on: false
+ # Locks down those users until they have been cleared by the admin (default: true)
block_auto_created_users: true
- # Auth providers
+ ## Auth providers
+ # Uncomment the lines and fill in the data of the auth provider you want to use
+ # If your favorite auth provider is not listed you can user others:
+ # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
providers:
# - { name: 'google_oauth2', app_id: 'YOUR APP ID',
# app_secret: 'YOUR APP SECRET',
@@ -60,29 +70,36 @@ omniauth:
# app_secret: 'YOUR APP SECRET' }
+
#
-# 3. Advanced settings:
+# 3. Advanced settings
# ==========================
-# Git Hosting configuration
-git_host:
+## Backup settings
+backup:
+ path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
+ # keep_time: 604800 # default: 0 (forever) (in seconds)
+
+## Gitolite settings
+gitolite:
admin_uri: git@localhost:gitolite-admin
- base_path: /home/git/repositories/
+ repos_path: /home/git/repositories/
hooks_path: /home/git/.gitolite/hooks/
- gitolite_admin_key: gitlab
- git_user: git
+ admin_key: gitlab
upload_pack: true
receive_pack: true
- # host: localhost
+ ssh_user: git
+ ssh_host: localhost
+ # ssh_port: 22
# config_file: gitolite.conf
- # port: 22
-# Git settings
-# Use default values unless you understand it
+## Git settings
+# CAUTION!
+# Use the default values unless you really know what you are doing
git:
- path: /usr/bin/git
+ bin_path: /usr/bin/git
# Max size of git object like commit, in bytes
# This value can be increased if you have a very large commits
- git_max_size: 5242880 # 5.megabytes
+ max_size: 5242880 # 5.megabytes
# Git timeout to read commit, in seconds
- git_timeout: 10
+ timeout: 10
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 32af3d07..4fe3ced4 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -2,23 +2,43 @@ class Settings < Settingslogic
source "#{Rails.root}/config/gitlab.yml"
class << self
+ # FIXME: Deprecated: remove for 4.1
def web_protocol
+ ActiveSupport::Deprecation.warn("Settings.web_protocol is deprecated and will be removed from GitLab 4.1", caller)
+ gitlab.protocol
+ rescue Settingslogic::MissingSetting
self.web['protocol'] ||= web.https ? "https" : "http"
end
+ # FIXME: Deprecated: remove for 4.1
def web_host
+ ActiveSupport::Deprecation.warn("Settings.web_host is deprecated and will be removed from GitLab 4.1", caller)
+ gitlab.host
+ rescue Settingslogic::MissingSetting
self.web['host'] ||= 'localhost'
end
+ # FIXME: Deprecated: remove for 4.1
def email_from
+ ActiveSupport::Deprecation.warn("Settings.email_from is deprecated and will be removed from GitLab 4.1", caller)
+ gitlab.email_from
+ rescue Settingslogic::MissingSetting
self.email['from'] ||= ("notify@" + web_host)
end
+ # FIXME: Deprecated: remove for 4.1
def url
+ ActiveSupport::Deprecation.warn("Settings.url is deprecated and will be removed from GitLab 4.1", caller)
+ gitlab.url
+ rescue Settingslogic::MissingSetting
self['url'] ||= build_url
end
+ # FIXME: Deprecated: remove for 4.1
def web_port
+ ActiveSupport::Deprecation.warn("Settings.web_port is deprecated and will be removed from GitLab 4.1", caller)
+ gitlab.port.to_i
+ rescue Settingslogic::MissingSetting
if web.https
web['port'] = 443
else
@@ -26,11 +46,17 @@ class Settings < Settingslogic
end.to_i
end
+ # FIXME: Deprecated: remove for 4.1
def web_custom_port?
+ ActiveSupport::Deprecation.warn("Settings.web_custom_port? is deprecated and will be removed from GitLab 4.1", caller)
+ gitlab_on_non_standard_port?
+ rescue Settingslogic::MissingSetting
![443, 80].include?(web_port)
end
+ # FIXME: Deprecated: remove for 4.1
def build_url
+ ActiveSupport::Deprecation.warn("Settings.build_url is deprecated and will be removed from GitLab 4.1", caller)
if web_custom_port?
custom_port = ":#{web_port}"
else
@@ -44,19 +70,35 @@ class Settings < Settingslogic
].join('')
end
+ # FIXME: Deprecated: remove for 4.1
def ssh_port
+ ActiveSupport::Deprecation.warn("Settings.ssh_port is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.ssh_port
+ rescue Settingslogic::MissingSetting
git_host['port'] || 22
end
+ # FIXME: Deprecated: remove for 4.1
def ssh_user
+ ActiveSupport::Deprecation.warn("Settings.ssh_user is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.ssh_user
+ rescue Settingslogic::MissingSetting
git_host['git_user'] || 'git'
end
+ # FIXME: Deprecated: remove for 4.1
def ssh_host
+ ActiveSupport::Deprecation.warn("Settings.ssh_host is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.ssh_host
+ rescue Settingslogic::MissingSetting
git_host['host'] || web_host || 'localhost'
end
+ # FIXME: Deprecated: remove for 4.1
def ssh_path
+ ActiveSupport::Deprecation.warn("Settings.ssh_path is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.ssh_path_prefix
+ rescue Settingslogic::MissingSetting
if ssh_port != 22
"ssh://#{ssh_user}@#{ssh_host}:#{ssh_port}/"
else
@@ -64,15 +106,27 @@ class Settings < Settingslogic
end
end
+ # FIXME: Deprecated: remove for 4.1
def git_base_path
+ ActiveSupport::Deprecation.warn("Settings.git_base_path is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.repos_path
+ rescue Settingslogic::MissingSetting
git_host['base_path'] || '/home/git/repositories/'
end
+ # FIXME: Deprecated: remove for 4.1
def git_hooks_path
+ ActiveSupport::Deprecation.warn("Settings.git_hooks_path is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.hooks_path
+ rescue Settingslogic::MissingSetting
git_host['hooks_path'] || '/home/git/share/gitolite/hooks/'
end
+ # FIXME: Deprecated: remove for 4.1
def git_upload_pack
+ ActiveSupport::Deprecation.warn("Settings.git_upload_pack is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.upload_pack
+ rescue Settingslogic::MissingSetting
if git_host['upload_pack'] != false
true
else
@@ -80,7 +134,11 @@ class Settings < Settingslogic
end
end
+ # FIXME: Deprecated: remove for 4.1
def git_receive_pack
+ ActiveSupport::Deprecation.warn("Settings.git_receive_pack is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.receive_pack
+ rescue Settingslogic::MissingSetting
if git_host['receive_pack'] != false
true
else
@@ -88,62 +146,207 @@ class Settings < Settingslogic
end
end
+ # FIXME: Deprecated: remove for 4.1
def git_bin_path
+ ActiveSupport::Deprecation.warn("Settings.git_bin_path is deprecated and will be removed from GitLab 4.1", caller)
+ git.bin_path
+ rescue Settingslogic::MissingSetting
git['path'] || '/usr/bin/git'
end
+ # FIXME: Deprecated: remove for 4.1
def git_max_size
+ ActiveSupport::Deprecation.warn("Settings.git_max_size is deprecated and will be removed from GitLab 4.1", caller)
+ git.max_size
+ rescue Settingslogic::MissingSetting
git['git_max_size'] || 5242880 # 5.megabytes
end
+ # FIXME: Deprecated: remove for 4.1
def git_timeout
+ ActiveSupport::Deprecation.warn("Settings.git_timeout is deprecated and will be removed from GitLab 4.1", caller)
+ git.timeout
+ rescue Settingslogic::MissingSetting
git['git_timeout'] || 10
end
+ # FIXME: Deprecated: remove for 4.1
def gitolite_admin_uri
+ ActiveSupport::Deprecation.warn("Settings.gitolite_admin_uri is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.admin_uri
+ rescue Settingslogic::MissingSetting
git_host['admin_uri'] || 'git@localhost:gitolite-admin'
end
+ # FIXME: Deprecated: remove for 4.1
def gitolite_config_file
+ ActiveSupport::Deprecation.warn("Settings.gitolite_config_file is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.config_file
+ rescue Settingslogic::MissingSetting
git_host['config_file'] || 'gitolite.conf'
end
+ # FIXME: Deprecated: remove for 4.1
def gitolite_admin_key
+ ActiveSupport::Deprecation.warn("Settings.gitolite_admin_key is deprecated and will be removed from GitLab 4.1", caller)
+ gitolite.admin_key
+ rescue Settingslogic::MissingSetting
git_host['gitolite_admin_key'] || 'gitlab'
end
+ # FIXME: Deprecated: remove for 4.1
def default_projects_limit
+ ActiveSupport::Deprecation.warn("Settings.default_projects_limit is deprecated and will be removed from GitLab 4.1", caller)
+ gitlab.default_projects_limit
+ rescue Settingslogic::MissingSetting
app['default_projects_limit'] || 10
end
+ # FIXME: Deprecated: remove for 4.1
def backup_path
- t = app['backup_path'] || "backups/"
- t = /^\//.match(t) ? t : Rails.root .join(t)
- t
+ ActiveSupport::Deprecation.warn("Settings.backup_path is deprecated and will be removed from GitLab 4.1", caller)
+ backup.path
+ rescue Settingslogic::MissingSetting
+ File.expand_path(app['backup_path'] || "backups/", Rails.root)
end
+ # FIXME: Deprecated: remove for 4.1
def backup_keep_time
+ ActiveSupport::Deprecation.warn("Settings.backup_keep_time is deprecated and will be removed from GitLab 4.1", caller)
+ backup.keep_time
+ rescue Settingslogic::MissingSetting
app['backup_keep_time'] || 0
end
+ # FIXME: Deprecated: remove for 4.1
def ldap_enabled?
- ldap && ldap['enabled']
+ ActiveSupport::Deprecation.warn("Settings.ldap_enabled? is deprecated and will be removed from GitLab 4.1", caller)
+ ldap.enabled
rescue Settingslogic::MissingSetting
false
end
+ # FIXME: Deprecated: remove for 4.1
def omniauth_enabled?
- omniauth && omniauth['enabled']
+ ActiveSupport::Deprecation.warn("Settings.omniauth_enabled? is deprecated and will be removed from GitLab 4.1", caller)
+ omniauth.enabled
rescue Settingslogic::MissingSetting
false
end
+ # FIXME: Deprecated: remove for 4.1
def omniauth_providers
- (omniauth_enabled? && omniauth['providers']) || []
+ ActiveSupport::Deprecation.warn("Settings.omniauth_providers is deprecated and will be removed from GitLab 4.1", caller)
+ omniauth.providers
+ rescue Settingslogic::MissingSetting
+ []
end
+ # FIXME: Deprecated: remove for 4.1
def disable_gravatar?
+ ActiveSupport::Deprecation.warn("Settings.disable_gravatar? is deprecated and will be removed from GitLab 4.1", caller)
+ !gravatar.enabled
+ rescue Settingslogic::MissingSetting
app['disable_gravatar'] || false
end
+
+ # FIXME: Deprecated: remove for 4.1
+ def gravatar_url
+ ActiveSupport::Deprecation.warn("Settings.gravatar_url is deprecated and will be removed from GitLab 4.1", caller)
+ gravatar.plain_url
+ rescue Settingslogic::MissingSetting
+ app['gravatar_url'] || 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm'
+ end
+
+ # FIXME: Deprecated: remove for 4.1
+ def gravatar_ssl_url
+ ActiveSupport::Deprecation.warn("Settings.gravatar_ssl_url is deprecated and will be removed from GitLab 4.1", caller)
+ gravatar.ssl_url
+ rescue Settingslogic::MissingSetting
+ app['gravatar_ssl_url'] || 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm'
+ end
+
+
+
+ def gitlab_on_non_standard_port?
+ ![443, 80].include?(gitlab.port.to_i)
+ end
+
+ private
+
+ def build_gitolite_ssh_path_prefix
+ if gitolite.ssh_port != 22
+ "ssh://#{gitolite.ssh_user}@#{gitolite.ssh_host}:#{gitolite.ssh_port}/"
+ else
+ "#{gitolite.ssh_user}@#{gitolite.ssh_host}:"
+ end
+ end
+
+ def build_gitlab_url
+ if gitlab_on_non_standard_port?
+ custom_port = ":#{gitlab.port}"
+ else
+ custom_port = nil
+ end
+ [ gitlab.protocol,
+ "://",
+ gitlab.host,
+ custom_port
+ ].join('')
+ end
end
end
+
+
+# Default settings
+
+# FIXME: Deprecated: remove for 4.1
+# all Settings.web ...
+# all Settings.app ...
+# all Settings.email ...
+# all Settings.git_host ...
+Settings['pre_40_config'] ||= Settings['web'].present?
+
+Settings['ldap'] ||= Settingslogic.new({})
+Settings.ldap['enabled'] ||= false
+
+Settings['omniauth'] ||= Settingslogic.new({})
+Settings.omniauth['enabled'] ||= false
+Settings.omniauth['providers'] ||= []
+
+Settings['gitlab'] ||= Settingslogic.new({})
+Settings.gitlab['default_projects_limit'] ||= Settings.pre_40_config ? Settings.default_projects_limit : 10
+Settings.gitlab['host'] ||= Settings.pre_40_config ? Settings.web_host : 'localhost'
+Settings.gitlab['https'] ||= Settings.pre_40_config ? Settings.web.https : false
+Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
+Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
+Settings.gitlab['email_from'] ||= Settings.pre_40_config ? Settings.email_from : "gitlab@#{Settings.gitlab.host}"
+Settings.gitlab['url'] ||= Settings.pre_40_config ? Settings.url : Settings.send(:build_gitlab_url)
+
+Settings['gravatar'] ||= Settingslogic.new({})
+Settings.gravatar['enabled'] ||= Settings.pre_40_config ? !Settings.disable_gravatar? : true
+Settings.gravatar['plain_url'] ||= Settings.pre_40_config ? Settings.gravatar_url : 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm'
+Settings.gravatar['ssl_url'] ||= Settings.pre_40_config ? Settings.gravatar_ssl_url : 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm'
+
+Settings['gitolite'] ||= Settingslogic.new({})
+Settings.gitolite['admin_key'] ||= Settings.pre_40_config ? Settings.gitolite_admin_key : 'gitlab'
+Settings.gitolite['admin_uri'] ||= Settings.pre_40_config ? Settings.gitolite_admin_uri : 'git@localhost:gitolite-admin'
+Settings.gitolite['config_file'] ||= Settings.pre_40_config ? Settings.gitolite_config_file : 'gitolite.conf'
+Settings.gitolite['hooks_path'] ||= Settings.pre_40_config ? Settings.git_hooks_path : '/home/git/share/gitolite/hooks/'
+Settings.gitolite['receive_pack'] ||= Settings.pre_40_config ? Settings.git_receive_pack : (Settings.gitolite['receive_pack'] != false)
+Settings.gitolite['repos_path'] ||= Settings.pre_40_config ? Settings.git_base_path : '/home/git/repositories/'
+Settings.gitolite['upload_pack'] ||= Settings.pre_40_config ? Settings.git_upload_pack : (Settings.gitolite['upload_pack'] != false)
+Settings.gitolite['ssh_host'] ||= Settings.pre_40_config ? Settings.ssh_host : (Settings.gitlab.host || 'localhost')
+Settings.gitolite['ssh_port'] ||= Settings.pre_40_config ? Settings.ssh_port : 22
+Settings.gitolite['ssh_user'] ||= Settings.pre_40_config ? Settings.ssh_user : 'git'
+Settings.gitolite['ssh_path_prefix'] ||= Settings.pre_40_config ? Settings.ssh_path : Settings.send(:build_gitolite_ssh_path_prefix)
+
+Settings['backup'] ||= Settingslogic.new({})
+Settings.backup['keep_time'] ||= Settings.pre_40_config ? Settings.backup_keep_time : 0
+Settings.backup['path'] = Settings.pre_40_config ? Settings.backup_path : File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root)
+
+Settings['git'] ||= Settingslogic.new({})
+Settings.git['max_size'] ||= Settings.pre_40_config ? Settings.git_max_size : 5242880 # 5.megabytes
+Settings.git['bin_path'] ||= Settings.pre_40_config ? Settings.git_bin_path : '/usr/bin/git'
+Settings.git['timeout'] ||= Settings.pre_40_config ? Settings.git_timeout : 10
+Settings.git['path'] ||= Settings.git.bin_path # FIXME: Deprecated: remove for 4.1
diff --git a/config/initializers/3_grit_ext.rb b/config/initializers/3_grit_ext.rb
index d114ea6c..097c301a 100644
--- a/config/initializers/3_grit_ext.rb
+++ b/config/initializers/3_grit_ext.rb
@@ -1,8 +1,8 @@
require 'grit'
require 'pygments'
-Grit::Git.git_timeout = Gitlab.config.git_timeout
-Grit::Git.git_max_size = Gitlab.config.git_max_size
+Grit::Git.git_timeout = Gitlab.config.git.timeout
+Grit::Git.git_max_size = Gitlab.config.git.max_size
Grit::Blob.class_eval do
include Linguist::BlobHelper
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 8f3cef5a..ed3ab718 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -4,7 +4,7 @@ Devise.setup do |config|
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class with default "from" parameter.
- config.mailer_sender = Gitlab.config.email_from
+ config.mailer_sender = Gitlab.config.gitlab.email_from
# Configure the class responsible to send e-mails.
# config.mailer = "Devise::Mailer"
@@ -205,20 +205,18 @@ Devise.setup do |config|
# manager.default_strategies(:scope => :user).unshift :some_external_strategy
# end
- gl = Gitlab.config
-
- if gl.ldap_enabled?
+ if Gitlab.config.ldap.enabled
config.omniauth :ldap,
- :host => gl.ldap['host'],
- :base => gl.ldap['base'],
- :uid => gl.ldap['uid'],
- :port => gl.ldap['port'],
- :method => gl.ldap['method'],
- :bind_dn => gl.ldap['bind_dn'],
- :password => gl.ldap['password']
+ :host => Gitlab.config.ldap['host'],
+ :base => Gitlab.config.ldap['base'],
+ :uid => Gitlab.config.ldap['uid'],
+ :port => Gitlab.config.ldap['port'],
+ :method => Gitlab.config.ldap['method'],
+ :bind_dn => Gitlab.config.ldap['bind_dn'],
+ :password => Gitlab.config.ldap['password']
end
- gl.omniauth_providers.each do |gl_provider|
- config.omniauth gl_provider['name'].to_sym, gl_provider['app_id'], gl_provider['app_secret']
+ Gitlab.config.omniauth.providers.each do |provider|
+ config.omniauth provider['name'].to_sym, provider['app_id'], provider['app_secret']
end
end
diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml
index a78cb6b6..3b763cf4 100644
--- a/config/locales/devise.en.yml
+++ b/config/locales/devise.en.yml
@@ -14,7 +14,7 @@ en:
devise:
failure:
already_authenticated: 'You are already signed in.'
- unauthenticated: 'You need to sign in or sign up before continuing.'
+ unauthenticated: 'You need to sign in before continuing.'
unconfirmed: 'You have to confirm your account before continuing.'
locked: 'Your account is locked.'
invalid: 'Invalid email or password.'
diff --git a/config/routes.rb b/config/routes.rb
index f1527977..e08bfebc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -14,10 +14,10 @@ Gitlab::Application.routes.draw do
# Enable Grack support
mount Grack::Bundle.new({
- git_path: Gitlab.config.git_bin_path,
- project_root: Gitlab.config.git_base_path,
- upload_pack: Gitlab.config.git_upload_pack,
- receive_pack: Gitlab.config.git_receive_pack
+ git_path: Gitlab.config.git.bin_path,
+ project_root: Gitlab.config.gitolite.repos_path,
+ upload_pack: Gitlab.config.gitolite.upload_pack,
+ receive_pack: Gitlab.config.gitolite.receive_pack
}), at: '/:path', constraints: { path: /[-\/\w\.-]+\.git/ }
#
@@ -164,11 +164,12 @@ Gitlab::Application.routes.draw do
end
end
- resources :merge_requests, constraints: {id: /\d+/} do
+ resources :merge_requests, constraints: {id: /\d+/}, except: [:destroy] do
member do
get :diffs
get :automerge
get :automerge_check
+ get :ci_status
end
collection do
@@ -199,9 +200,9 @@ Gitlab::Application.routes.draw do
:via => [:get, :post], constraints: {from: /.+/, to: /.+/}
resources :team, controller: 'team_members', only: [:index]
- resources :milestones
+ resources :milestones, except: [:destroy]
resources :labels, only: [:index]
- resources :issues do
+ resources :issues, except: [:destroy] do
collection do
post :sort
post :bulk_update
diff --git a/db/fixtures/development/002_project.rb b/db/fixtures/development/002_project.rb
index 91d42a14..4db11a87 100644
--- a/db/fixtures/development/002_project.rb
+++ b/db/fixtures/development/002_project.rb
@@ -1,5 +1,14 @@
-Project.seed(:id, [
- { id: 1, name: "Underscore.js", path: "underscore", owner_id: 1, namespace_id: 1 },
- { id: 2, name: "Diaspora", path: "diaspora", owner_id: 1 },
- { id: 3, name: "Ruby on Rails", path: "rails", owner_id: 1 }
+Group.seed(:id, [
+ { id: 100, name: "Brightbox", path: 'brightbox', owner_id: 1 },
+ { id: 101, name: "KDE", path: 'kde', owner_id: 1 },
+])
+
+Project.seed(:id, [
+ { id: 1, name: "Underscore.js", path: "underscore", owner_id: 1 },
+ { id: 2, name: "Diaspora", path: "diaspora", owner_id: 1 },
+ { id: 3, namespace_id: 100, name: "Brightbox CLI", path: "brightbox-cli", owner_id: 1 },
+ { id: 4, namespace_id: 100, name: "Puppet", path: "puppet", owner_id: 1 },
+ { id: 5, namespace_id: 101, name: "kdebase", path: "kdebase", owner_id: 1},
+ { id: 6, namespace_id: 101, name: "kdelibs", path: "kdelibs", owner_id: 1},
+ { id: 7, namespace_id: 101, name: "amarok", path: "amarok", owner_id: 1},
])
diff --git a/db/fixtures/development/009_source_code.rb b/db/fixtures/development/009_source_code.rb
index 849d1aab..6b9b6584 100644
--- a/db/fixtures/development/009_source_code.rb
+++ b/db/fixtures/development/009_source_code.rb
@@ -1,9 +1,10 @@
root = Gitlab.config.git_base_path
projects = [
- { path: 'root/underscore.git', git: 'https://github.com/documentcloud/underscore.git' },
+ { path: 'underscore.git', git: 'https://github.com/documentcloud/underscore.git' },
{ path: 'diaspora.git', git: 'https://github.com/diaspora/diaspora.git' },
- { path: 'rails.git', git: 'https://github.com/rails/rails.git' },
+ { path: 'brightbox/brightbox-cli.git', git: 'https://github.com/brightbox/brightbox-cli.git' },
+ { path: 'brightbox/puppet.git', git: 'https://github.com/brightbox/puppet.git' },
]
projects.each do |project|
@@ -14,7 +15,7 @@ projects.each do |project|
cmds = [
"cd #{root} && sudo -u git -H git clone --bare #{project[:git]} ./#{project[:path]}",
- "sudo cp ./lib/hooks/post-receive #{project_path}/hooks/post-receive",
+ "sudo ln -s ./lib/hooks/post-receive #{project_path}/hooks/post-receive",
"sudo chown git:git -R #{project_path}",
"sudo chmod 770 -R #{project_path}",
]
diff --git a/db/fixtures/development/010_groups.rb b/db/fixtures/development/010_groups.rb
deleted file mode 100644
index 09371b00..00000000
--- a/db/fixtures/development/010_groups.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-Group.seed(:id, [
- { id: 100, name: "Gitlab", path: 'gitlab', owner_id: 1},
- { id: 101, name: "Rails", path: 'rails', owner_id: 1 },
- { id: 102, name: "KDE", path: 'kde', owner_id: 1 }
-])
-
-Project.seed(:id, [
- { id: 10, name: "kdebase", path: "kdebase", owner_id: 1, namespace_id: 102 },
- { id: 11, name: "kdelibs", path: "kdelibs", owner_id: 1, namespace_id: 102 },
- { id: 12, name: "amarok", path: "amarok", owner_id: 1, namespace_id: 102 }
-])
diff --git a/db/migrate/20121205201726_add_more_indexes.rb b/db/migrate/20121205201726_add_more_indexes.rb
new file mode 100644
index 00000000..a2b36f7f
--- /dev/null
+++ b/db/migrate/20121205201726_add_more_indexes.rb
@@ -0,0 +1,44 @@
+class AddMoreIndexes < ActiveRecord::Migration
+ def change
+ add_index :events, :created_at
+ add_index :events, :target_id
+
+ add_index :issues, :closed
+ add_index :issues, :created_at
+ add_index :issues, :title
+
+ add_index :keys, :identifier
+ # FIXME: MySQL can't index text columns
+ #add_index :keys, :key
+ add_index :keys, :project_id
+
+ add_index :merge_requests, :closed
+ add_index :merge_requests, :created_at
+ add_index :merge_requests, :source_branch
+ add_index :merge_requests, :target_branch
+ add_index :merge_requests, :title
+
+ add_index :milestones, :due_date
+ add_index :milestones, :project_id
+
+ add_index :namespaces, :name
+ add_index :namespaces, :path
+ add_index :namespaces, :type
+
+ add_index :notes, :created_at
+
+ add_index :snippets, :created_at
+ add_index :snippets, :expires_at
+
+ add_index :users, :admin
+ add_index :users, :blocked
+ add_index :users, :name
+ add_index :users, :username
+
+ add_index :users_projects, :project_access
+ add_index :users_projects, :user_id
+
+ add_index :wikis, :project_id
+ add_index :wikis, :slug
+ end
+end
diff --git a/db/migrate/20121218164840_move_noteable_commit_to_own_field.rb b/db/migrate/20121218164840_move_noteable_commit_to_own_field.rb
new file mode 100644
index 00000000..6f2da413
--- /dev/null
+++ b/db/migrate/20121218164840_move_noteable_commit_to_own_field.rb
@@ -0,0 +1,20 @@
+class MoveNoteableCommitToOwnField < ActiveRecord::Migration
+ def up
+ add_column :notes, :commit_id, :string, null: true
+ add_column :notes, :new_noteable_id, :integer, null: true
+ Note.where(noteable_type: 'Commit').update_all('commit_id = noteable_id')
+
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
+ Note.where("noteable_type != 'Commit'").update_all('new_noteable_id = CAST (noteable_id AS INTEGER)')
+ else
+ Note.where("noteable_type != 'Commit'").update_all('new_noteable_id = noteable_id')
+ end
+
+ remove_column :notes, :noteable_id
+ rename_column :notes, :new_noteable_id, :noteable_id
+ end
+
+ def down
+ raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/migrate/20121219095402_indices_for_notes.rb b/db/migrate/20121219095402_indices_for_notes.rb
new file mode 100644
index 00000000..4c5d041c
--- /dev/null
+++ b/db/migrate/20121219095402_indices_for_notes.rb
@@ -0,0 +1,6 @@
+class IndicesForNotes < ActiveRecord::Migration
+ def change
+ add_index :notes, :commit_id
+ add_index :notes, [:project_id, :noteable_type]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1abfcb46..7de55932 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20121203160507) do
+ActiveRecord::Schema.define(:version => 20121219095402) do
create_table "events", :force => true do |t|
t.string "target_type"
@@ -27,7 +27,9 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
add_index "events", ["action"], :name => "index_events_on_action"
add_index "events", ["author_id"], :name => "index_events_on_author_id"
+ add_index "events", ["created_at"], :name => "index_events_on_created_at"
add_index "events", ["project_id"], :name => "index_events_on_project_id"
+ add_index "events", ["target_id"], :name => "index_events_on_target_id"
add_index "events", ["target_type"], :name => "index_events_on_target_type"
create_table "issues", :force => true do |t|
@@ -46,8 +48,11 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id"
add_index "issues", ["author_id"], :name => "index_issues_on_author_id"
+ add_index "issues", ["closed"], :name => "index_issues_on_closed"
+ add_index "issues", ["created_at"], :name => "index_issues_on_created_at"
add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id"
add_index "issues", ["project_id"], :name => "index_issues_on_project_id"
+ add_index "issues", ["title"], :name => "index_issues_on_title"
create_table "keys", :force => true do |t|
t.integer "user_id"
@@ -59,6 +64,8 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.integer "project_id"
end
+ add_index "keys", ["identifier"], :name => "index_keys_on_identifier"
+ add_index "keys", ["project_id"], :name => "index_keys_on_project_id"
add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
create_table "merge_requests", :force => true do |t|
@@ -80,8 +87,13 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id"
+ add_index "merge_requests", ["closed"], :name => "index_merge_requests_on_closed"
+ add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at"
add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id"
add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id"
+ add_index "merge_requests", ["source_branch"], :name => "index_merge_requests_on_source_branch"
+ add_index "merge_requests", ["target_branch"], :name => "index_merge_requests_on_target_branch"
+ add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title"
create_table "milestones", :force => true do |t|
t.string "title", :null => false
@@ -93,6 +105,9 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.datetime "updated_at", :null => false
end
+ add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date"
+ add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id"
+
create_table "namespaces", :force => true do |t|
t.string "name", :null => false
t.string "path", :null => false
@@ -102,11 +117,13 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.string "type"
end
+ add_index "namespaces", ["name"], :name => "index_namespaces_on_name"
add_index "namespaces", ["owner_id"], :name => "index_namespaces_on_owner_id"
+ add_index "namespaces", ["path"], :name => "index_namespaces_on_path"
+ add_index "namespaces", ["type"], :name => "index_namespaces_on_type"
create_table "notes", :force => true do |t|
t.text "note"
- t.string "noteable_id"
t.string "noteable_type"
t.integer "author_id"
t.datetime "created_at", :null => false
@@ -114,10 +131,14 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.integer "project_id"
t.string "attachment"
t.string "line_code"
+ t.string "commit_id"
+ t.integer "noteable_id"
end
- add_index "notes", ["noteable_id"], :name => "index_notes_on_noteable_id"
+ add_index "notes", ["commit_id"], :name => "index_notes_on_commit_id"
+ add_index "notes", ["created_at"], :name => "index_notes_on_created_at"
add_index "notes", ["noteable_type"], :name => "index_notes_on_noteable_type"
+ add_index "notes", ["project_id", "noteable_type"], :name => "index_notes_on_project_id_and_noteable_type"
add_index "notes", ["project_id"], :name => "index_notes_on_project_id"
create_table "projects", :force => true do |t|
@@ -170,6 +191,8 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.datetime "expires_at"
end
+ add_index "snippets", ["created_at"], :name => "index_snippets_on_created_at"
+ add_index "snippets", ["expires_at"], :name => "index_snippets_on_expires_at"
add_index "snippets", ["project_id"], :name => "index_snippets_on_project_id"
create_table "taggings", :force => true do |t|
@@ -220,9 +243,13 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.string "username"
end
+ add_index "users", ["admin"], :name => "index_users_on_admin"
+ add_index "users", ["blocked"], :name => "index_users_on_blocked"
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true
+ add_index "users", ["name"], :name => "index_users_on_name"
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
+ add_index "users", ["username"], :name => "index_users_on_username"
create_table "users_projects", :force => true do |t|
t.integer "user_id", :null => false
@@ -232,7 +259,9 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.integer "project_access", :default => 0, :null => false
end
+ add_index "users_projects", ["project_access"], :name => "index_users_projects_on_project_access"
add_index "users_projects", ["project_id"], :name => "index_users_projects_on_project_id"
+ add_index "users_projects", ["user_id"], :name => "index_users_projects_on_user_id"
create_table "web_hooks", :force => true do |t|
t.string "url"
@@ -253,4 +282,7 @@ ActiveRecord::Schema.define(:version => 20121203160507) do
t.integer "user_id"
end
+ add_index "wikis", ["project_id"], :name => "index_wikis_on_project_id"
+ add_index "wikis", ["slug"], :name => "index_wikis_on_slug"
+
end
diff --git a/doc/api/README.md b/doc/api/README.md
index ca346418..477429c9 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -15,7 +15,7 @@ API requests should be prefixed with `api` and the API version. The API version
Example of a valid API request:
```
-GET http://example.com/api/v2/projects?private_token=QVy1PB7sTxfy4pqfZM1U
+GET http://example.com/api/v3/projects?private_token=QVy1PB7sTxfy4pqfZM1U
```
The API uses JSON to serialize data. You don't need to specify `.json` at the end of API URL.
diff --git a/doc/api/issues.md b/doc/api/issues.md
index aaad3305..0383b676 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -18,6 +18,7 @@ GET /issues
"assignee": null,
"author": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -46,6 +47,7 @@ GET /issues
},
"assignee": {
"id": 2,
+ "username": "jack_smith",
"email": "jack@example.com",
"name": "Jack Smith",
"blocked": false,
@@ -53,6 +55,7 @@ GET /issues
},
"author": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -75,7 +78,7 @@ GET /projects/:id/issues
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
## Single issue
@@ -87,7 +90,7 @@ GET /projects/:id/issues/:issue_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of a project issue
```json
@@ -110,6 +113,7 @@ Parameters:
},
"assignee": {
"id": 2,
+ "username": "jack_smith",
"email": "jack@example.com",
"name": "Jack Smith",
"blocked": false,
@@ -117,6 +121,7 @@ Parameters:
},
"author": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -138,7 +143,7 @@ POST /projects/:id/issues
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `title` (required) - The title of an issue
+ `description` (optional) - The description of an issue
+ `assignee_id` (optional) - The ID of a user to assign issue
@@ -157,7 +162,7 @@ PUT /projects/:id/issues/:issue_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of a project's issue
+ `title` (optional) - The title of an issue
+ `description` (optional) - The description of an issue
@@ -168,17 +173,3 @@ Parameters:
Will return updated issue with status `200 OK` on success, or `404 Not found` on fail.
-## Delete issue
-
-Delete existing project issue.
-
-```
-DELETE /projects/:id/issues/:issue_id
-```
-
-Parameters:
-
-+ `id` (required) - The ID or code name of a project
-+ `issue_id` (required) - The ID of a project's issue
-
-Status code `200` will be returned on success.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index e5b067a6..525c55d1 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -8,7 +8,7 @@ GET /projects/:id/merge_requests
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
```json
[
@@ -22,6 +22,7 @@ Parameters:
"merged":false,
"author":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -29,6 +30,7 @@ Parameters:
},
"assignee":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -48,7 +50,7 @@ GET /projects/:id/merge_request/:merge_request_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `merge_request_id` (required) - The ID of MR
```json
@@ -62,6 +64,7 @@ Parameters:
"merged":false,
"author":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -69,6 +72,7 @@ Parameters:
},
"assignee":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -88,7 +92,7 @@ POST /projects/:id/merge_requests
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `source_branch` (required) - The source branch
+ `target_branch` (required) - The target branch
+ `assignee_id` - Assignee user ID
@@ -105,6 +109,7 @@ Parameters:
"merged":false,
"author":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -112,6 +117,7 @@ Parameters:
},
"assignee":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -130,7 +136,7 @@ PUT /projects/:id/merge_request/:merge_request_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `merge_request_id` (required) - ID of MR
+ `source_branch` - The source branch
+ `target_branch` - The target branch
@@ -150,6 +156,7 @@ Parameters:
"merged":false,
"author":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -157,6 +164,7 @@ Parameters:
},
"assignee":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
@@ -174,7 +182,7 @@ POST /projects/:id/merge_request/:merge_request_id/comments
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `merge_request_id` (required) - ID of MR
+ `note` (required) - Text of comment
@@ -184,6 +192,7 @@ Will return created note with status `201 Created` on success, or `404 Not found
{
"author":{
"id":1,
+ "username": "admin",
"email":"admin@local.host",
"name":"Administrator",
"blocked":false,
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index f68d8eb7..b997e839 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -8,7 +8,7 @@ GET /projects/:id/milestones
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
## Single milestone
@@ -20,7 +20,7 @@ GET /projects/:id/milestones/:milestone_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `milestone_id` (required) - The ID of a project milestone
## New milestone
@@ -33,7 +33,7 @@ POST /projects/:id/milestones
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `milestone_id` (required) - The ID of a project milestone
+ `title` (required) - The title of an milestone
+ `description` (optional) - The description of the milestone
@@ -49,7 +49,7 @@ PUT /projects/:id/milestones/:milestone_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `milestone_id` (required) - The ID of a project milestone
+ `title` (optional) - The title of a milestone
+ `description` (optional) - The description of a milestone
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 7b226dea..bb33efb8 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -15,6 +15,7 @@ GET /projects/:id/notes
"body": "The solution is rather tricky",
"author": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -27,7 +28,7 @@ GET /projects/:id/notes
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
### List issue notes
@@ -39,7 +40,7 @@ GET /projects/:id/issues/:issue_id/notes
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of an issue
### List snippet notes
@@ -52,7 +53,7 @@ GET /projects/:id/snippets/:snippet_id/notes
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a snippet
## Single note
@@ -67,7 +68,7 @@ GET /projects/:id/notes/:note_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `note_id` (required) - The ID of a wall note
### Single issue note
@@ -80,7 +81,7 @@ GET /projects/:id/issues/:issue_id/:notes/:note_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of a project issue
+ `note_id` (required) - The ID of an issue note
@@ -94,7 +95,7 @@ GET /projects/:id/issues/:snippet_id/:notes/:note_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project snippet
+ `note_id` (required) - The ID of an snippet note
@@ -110,7 +111,7 @@ POST /projects/:id/notes
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `body` (required) - The content of a note
Will return created note with status `201 Created` on success, or `404 Not found` on fail.
@@ -126,7 +127,7 @@ POST /projects/:id/issues/:issue_id/notes
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of an issue
+ `body` (required) - The content of a note
@@ -142,7 +143,7 @@ POST /projects/:id/snippets/:snippet_id/notes
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of an snippet
+ `body` (required) - The content of a note
diff --git a/doc/api/projects.md b/doc/api/projects.md
index fdedf904..41128675 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -10,13 +10,12 @@ GET /projects
[
{
"id": 3,
- "code": "rails",
"name": "rails",
"description": null,
- "path": "rails",
"default_branch": "master",
"owner": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -31,13 +30,12 @@ GET /projects
},
{
"id": 5,
- "code": "gitlab",
"name": "gitlab",
"description": null,
- "path": "gitlab",
"default_branch": "api",
"owner": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -63,18 +61,17 @@ GET /projects/:id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
```json
{
"id": 5,
- "code": "gitlab",
"name": "gitlab",
"description": null,
- "path": "gitlab",
"default_branch": "api",
"owner": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -100,8 +97,6 @@ POST /projects
Parameters:
+ `name` (required) - new project name
-+ `code` (optional) - new project code, 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
@@ -122,7 +117,8 @@ GET /projects/:id/members
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
++ `query` - Query string
## Get project team member
@@ -134,13 +130,14 @@ GET /projects/:id/members/:user_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `user_id` (required) - The ID of a user
```json
{
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -159,7 +156,7 @@ POST /projects/:id/members
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `user_id` (required) - The ID of a user to add
+ `access_level` (required) - Project access level
@@ -175,7 +172,7 @@ PUT /projects/:id/members/:user_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `user_id` (required) - The ID of a team member
+ `access_level` (required) - Project access level
@@ -191,7 +188,7 @@ DELETE /projects/:id/members/:user_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `user_id` (required) - The ID of a team member
Status code `200` will be returned on success.
@@ -206,7 +203,7 @@ GET /projects/:id/hooks
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
Will return hooks with status `200 OK` on success, or `404 Not found` on fail.
@@ -220,7 +217,7 @@ GET /projects/:id/hooks/:hook_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `hook_id` (required) - The ID of a project hook
Will return hook with status `200 OK` on success, or `404 Not found` on fail.
@@ -235,7 +232,7 @@ POST /projects/:id/hooks
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `url` (required) - The hook URL
Will return status `201 Created` on success, or `404 Not found` on fail.
@@ -250,7 +247,7 @@ PUT /projects/:id/hooks/:hook_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `hook_id` (required) - The ID of a project hook
+ `url` (required) - The hook URL
@@ -267,7 +264,7 @@ DELETE /projects/:id/hooks
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `hook_id` (required) - The ID of hook to delete
Will return status `200 OK` on success, or `404 Not found` on fail.
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 487ad9b2..685797ad 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -8,7 +8,7 @@ GET /projects/:id/repository/branches
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
```json
[
@@ -48,7 +48,7 @@ GET /projects/:id/repository/branches/:branch
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `branch` (required) - The name of the branch
```json
@@ -87,7 +87,7 @@ GET /projects/:id/repository/tags
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
```json
[
@@ -125,7 +125,7 @@ GET /projects/:id/repository/commits
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `ref_name` (optional) - The name of a repository branch or tag
```json
@@ -159,7 +159,7 @@ GET /projects/:id/repository/commits/:sha/blob
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `sha` (required) - The commit or branch name
+ `filepath` (required) - The path the file
diff --git a/doc/api/session.md b/doc/api/session.md
index 9fdbeb43..c7e57aac 100644
--- a/doc/api/session.md
+++ b/doc/api/session.md
@@ -13,6 +13,7 @@ Parameters:
```json
{
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"private_token": "dd34asd13as",
diff --git a/doc/api/snippets.md b/doc/api/snippets.md
index 288fd529..ceb8a63d 100644
--- a/doc/api/snippets.md
+++ b/doc/api/snippets.md
@@ -8,7 +8,7 @@ GET /projects/:id/snippets
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
## Single snippet
@@ -20,7 +20,7 @@ GET /projects/:id/snippets/:snippet_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project's snippet
```json
@@ -30,6 +30,7 @@ Parameters:
"file_name": "add.rb",
"author": {
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -51,7 +52,7 @@ GET /projects/:id/snippets/:snippet_id/raw
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project's snippet
## New snippet
@@ -64,7 +65,7 @@ POST /projects/:id/snippets
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `title` (required) - The title of a snippet
+ `file_name` (required) - The name of a snippet file
+ `lifetime` (optional) - The expiration date of a snippet
@@ -82,7 +83,7 @@ PUT /projects/:id/snippets/:snippet_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project's snippet
+ `title` (optional) - The title of a snippet
+ `file_name` (optional) - The name of a snippet file
@@ -101,7 +102,7 @@ DELETE /projects/:id/snippets/:snippet_id
Parameters:
-+ `id` (required) - The ID or code name of a project
++ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project's snippet
Status code `200` will be returned on success.
diff --git a/doc/api/users.md b/doc/api/users.md
index c116144d..200c0e06 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -10,6 +10,7 @@ GET /users
[
{
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -23,6 +24,7 @@ GET /users
},
{
"id": 2,
+ "username": "jack_smith",
"email": "jack@example.com",
"name": "Jack Smith",
"blocked": false,
@@ -52,6 +54,7 @@ Parameters:
```json
{
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
@@ -75,7 +78,8 @@ POST /users
Parameters:
+ `email` (required) - Email
+ `password` (required) - Password
-+ `name` - Name
++ `username` (required) - Username
++ `name` (required) - Name
+ `skype` - Skype ID
+ `linkedin` - Linkedin
+ `twitter` - Twitter account
@@ -95,6 +99,7 @@ GET /user
```json
{
"id": 1,
+ "username": "john_smith",
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
diff --git a/doc/development.md b/doc/development.md
deleted file mode 100644
index b7213adc..00000000
--- a/doc/development.md
+++ /dev/null
@@ -1,36 +0,0 @@
-## Development tips:
-
-
-### Installation
-
-Install the Gitlab development in a virtual machine with the [Gitlab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm). Installing it in a virtual machine makes it much easier to set up all the dependencies for integration testing.
-
-
-### Start application in development mode
-
-#### 1. Via foreman
-
- bundle exec foreman start -p 3000
-
-#### 2. Manually
-
- bundle exec rails s
- bundle exec rake environment resque:work QUEUE=* VVERBOSE=1
-
-
-### Test DB setup & seed
-
- bundle exec rake db:setup RAILS_ENV=test
- bundle exec rake db:seed_fu RAILS_ENV=test
-
-
-### Run the Tests
-
- # All in one
- bundle exec rake gitlab:test
-
- # Rspec
- bundle exec rake spec
-
- # Spinach
- bundle exec rake spinach
diff --git a/doc/install/databases.md b/doc/install/databases.md
index 1a6f739e..4c6c084d 100644
--- a/doc/install/databases.md
+++ b/doc/install/databases.md
@@ -1,60 +1,51 @@
-# Databases:
+# Setup Database
-GitLab use MySQL as default database but you are free to use PostgreSQL.
+GitLab supports the following databases:
+
+* MySQL (preferred)
+* PostgreSQL
## MySQL
+ # Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
# Login to MySQL
$ mysql -u root -p
+ # Create a user for GitLab. (change $password to a real password)
+ mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password';
+
# Create the GitLab production database
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
- mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password';
-
- # Grant proper permissions to the MySQL User
+ # Grant the GitLab user necessary permissopns on the table.
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost';
+ # Quit the database session
+ mysql> \q
+
+ # Try connecting to the new database with the new user
+ sudo -u gitlab -H mysql -u gitlab -p -D gitlabhq_production
## PostgreSQL
- sudo apt-get install -y postgresql-9.1 postgresql-server-dev-9.1
+ # Install the database packages
+ sudo apt-get install -y postgresql-9.1 libpq-dev
- # Connect to database server
+ # Login to PostgreSQL
sudo -u postgres psql -d template1
- # Add a user called gitlab. Change $password to a real password
+ # Create a user for GitLab. (change $password to a real password)
template1=# CREATE USER gitlab WITH PASSWORD '$password';
# Create the GitLab production database & grant all privileges on database
template1=# CREATE DATABASE gitlabhq_production OWNER gitlab;
- # Quit from PostgreSQL server
+ # Quit the database session
template1=# \q
- # Try connect to new database
- sudo -u gitlab psql -d gitlabhq_production
+ # Try connecting to the new database with the new user
+ sudo -u gitlab -H psql -d gitlabhq_production
-
-
-#### Select the database you want to use
-
- # Mysql
- sudo -u gitlab cp config/database.yml.mysql config/database.yml
-
- # PostgreSQL
- sudo -u gitlab cp config/database.yml.postgresql config/database.yml
-
- # make sure to update username/password in config/database.yml
-
-#### Install gems
-
- # mysql
- sudo -u gitlab -H bundle install --without development test postgres --deployment
-
- # or postgres
- sudo -u gitlab -H bundle install --without development test mysql --deployment
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 6876a875..b872ceaf 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -1,283 +1,344 @@
-_This installation guide created for Debian/Ubuntu and properly tested._
+This installation guide was created for Debian/Ubuntu and tested on it.
-_Checkout requirements before setup_
+Please read `doc/install/requirements.md` for hardware and platform requirements.
-### IMPORTANT
+**Important Note:**
+The following steps have been known to work.
+If you deviate from this guide, do it with caution and make sure you don't
+violate any assumptions GitLab makes about its environment.
+For things like AWS installation scripts, init scripts or config files for
+alternative web server have a look at the "Advanced Setup Tips" section.
-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.
-
-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.
+**Important Note:**
+If you find a bug/error in this guide please submit an issue or pull request
+following the contribution guide (see `CONTRIBUTING.md`).
- - -
-# Basic setup
+# Overview
-The basic installation will provide you a GitLab setup with options:
+The GitLab installation consists of setting up th following components:
-1. ruby 1.9.3
-2. mysql as main db
-3. gitolite v3 fork by gitlab
-4. nginx + unicorn
-
-The installation consists of next steps:
-
-1. Packages / dependencies
+1. Packages / Dependencies
2. Ruby
-3. Users
+3. System Users
4. Gitolite
-5. Mysql
-6. GitLab.
-7. Nginx
+5. Database
+6. GitLab
+7. Nginx
-# 1. Packages / dependencies
+# 1. Packages / Dependencies
-*Keep in mind that `sudo` is not installed on Debian by default. You should install it as root:*
+`sudo` is not installed on Debian by default. If you don't have it you'll need
+to install it first.
+ # run as root
apt-get update && apt-get upgrade && apt-get install sudo
-Now install the required packages:
+Make sure your system is up-to-date:
sudo apt-get update
sudo apt-get upgrade
- sudo apt-get install -y wget curl gcc checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libreadline6-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev libicu-dev redis-server openssh-server git-core python-dev python-pip libyaml-dev postfix libpq-dev
+**Note:**
+Vim is an editor that is used here whenever there are files that need to be
+edited by hand. But, you can use any editor you like instead.
- sudo pip install pygments
+ # Install vim
+ sudo apt-get install -y vim
+
+Install the required packages:
+
+ sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev wget curl git-core openssh-server redis-server postfix checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev
+
+Make sure you have the right version of Python installed.
+
+ # Install Python
+ sudo apt-get install python
+
+ # Make sure that Python is 2.5+ (3.x is not supported at the moment)
+ python --version
+
+ # If it's Python 3 you might need to install Python 2 separately
+ sudo apt-get install python2.7
+
+ # Make sure you can access Python via python2
+ python2 --version
+
+ # If you get a "command not found" error create a link to the python binary
+ sudo ln -s /usr/bin/python /usr/bin/python2
-# 2. Install Ruby
+# 2. Ruby
- 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
+Download and compile it:
+
+ mkdir /tmp/ruby && cd /tmp/ruby
+ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p327.tar.gz
+ tar xfvz ruby-1.9.3-p327.tar.gz
+ cd ruby-1.9.3-p327
./configure
make
sudo make install
-# 3. Users
+Install the Bundler Gem:
-Create user for git:
+ sudo gem install bundler
+
+
+# 3. System Users
+
+Create a user for Git and Gitolite:
sudo adduser \
--system \
--shell /bin/sh \
- --gecos 'git version control' \
+ --gecos 'Git Version Control' \
--group \
--disabled-password \
--home /home/git \
git
-Create user for GitLab:
+Create a user for GitLab:
- # ubuntu/debian
- sudo adduser --disabled-login --gecos 'gitlab system' gitlab
-
-Add your users to groups:
+ sudo adduser --disabled-login --gecos 'GitLab' gitlab
+ # Add it to the git group
sudo usermod -a -G git gitlab
- sudo usermod -a -G gitlab git
-Generate key:
-
- sudo -H -u gitlab ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa
+ # Generate the SSH key
+ sudo -u gitlab -H ssh-keygen -q -N '' -t rsa -f /home/gitlab/.ssh/id_rsa
# 4. Gitolite
Clone GitLab's fork of the Gitolite source code:
- sudo -H -u git git clone -b gl-v304 https://github.com/gitlabhq/gitolite.git /home/git/gitolite
-
-Setup:
-
cd /home/git
- sudo -u git -H mkdir bin
- sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" >> /home/git/.profile'
- sudo -u git sh -c 'gitolite/install -ln /home/git/bin'
+ sudo -u git -H git clone -b gl-v320 https://github.com/gitlabhq/gitolite.git /home/git/gitolite
+Setup Gitolite with GitLab as its admin:
+
+**Important Note:**
+GitLab assumes *full and unshared* control over this Gitolite installation.
+
+ # Add Gitolite scripts to $PATH
+ sudo -u git -H mkdir /home/git/bin
+ sudo -u git -H sh -c 'printf "%b\n%b\n" "PATH=\$PATH:/home/git/bin" "export PATH" >> /home/git/.profile'
+ sudo -u git -H sh -c 'gitolite/install -ln /home/git/bin'
+
+ # Copy the gitlab user's (public) SSH key ...
sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub
sudo chmod 0444 /home/git/gitlab.pub
+ # ... and use it as the admin key for the Gitolite setup
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gitolite setup -pk /home/git/gitlab.pub"
-
-Permissions:
+Fix the directory permissions for the configuration directory:
- sudo chmod -R g+rwX /home/git/repositories/
+ # Make sure the Gitolite config dir is owned by git
+ sudo chmod 750 /home/git/.gitolite/
+ sudo chown -R git:git /home/git/.gitolite/
+
+Fix the directory permissions for the repositories:
+
+ # Make sure the repositories dir is owned by git and it stays that way
+ sudo chmod -R ug+rwXs,o-rwx /home/git/repositories/
sudo chown -R git:git /home/git/repositories/
- # clone admin repo to add localhost to known_hosts
- # & be sure your user has access to gitolite
+## Test if everything works so far
+
+ # Clone the admin repo so SSH adds localhost to known_hosts ...
+ # ... and to be sure your users have access to Gitolite
sudo -u gitlab -H git clone git@localhost:gitolite-admin.git /tmp/gitolite-admin
- # if succeed you can remove it
+ # If it succeeded without errors you can remove the cloned repo
sudo rm -rf /tmp/gitolite-admin
-**IMPORTANT! If you can't clone `gitolite-admin` repository - DO NOT PROCEED WITH INSTALLATION**
+**Important Note:**
+If you can't clone the `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.
+and make sure you have followed all of the above steps carefully.
-# 5. Mysql database
+# 5. Database
- sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
-
- # Login to MySQL
- $ mysql -u root -p
-
- # Create the GitLab production database
- 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
- mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password';
-
- # Grant proper permissions to the MySQL User
- mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost';
+See `doc/install/databases.md`
# 6. GitLab
+ # We'll install GitLab into home directory of the user "gitlab"
cd /home/gitlab
+## Clone the Source
-#### Get source code
+ # Clone the latest stable release
+ sudo -u gitlab -H git clone -b stable https://github.com/gitlabhq/gitlabhq.git gitlab
- # Get gitlab code. Use this for stable setup
- sudo -H -u gitlab git clone -b stable https://github.com/gitlabhq/gitlabhq.git gitlab
+**Note:**
+You can change `stable` to `master` if you want the *bleeding edge* version, but
+do so with caution!
- # Skip this for stable setup.
- # Master branch (recent changes, less stable)
- sudo -H -u gitlab git clone -b master https://github.com/gitlabhq/gitlabhq.git gitlab
+## Configure it
+ cd /home/gitlab/gitlab
-#### Copy configs
-
- cd gitlab
+ # Copy the example GitLab config
+ sudo -u gitlab -H cp config/gitlab.yml.example config/gitlab.yml
- # Rename config files
- #
- sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml
+ # Make sure to change "localhost" to the fully-qualified domain name of your
+ # host serving GitLab where necessary
+ sudo -u gitlab -H vim config/gitlab.yml
- # Copy mysql db config
- #
- # make sure to update username/password in config/database.yml
- #
+ # Make sure GitLab can write to the log/ and tmp/ directories
+ sudo chown -R gitlab log/
+ sudo chown -R gitlab tmp/
+ sudo chmod -R u+rwX log/
+ sudo chmod -R u+rwX tmp/
+
+ # Copy the example Unicorn config
+ sudo -u gitlab -H cp config/unicorn.rb.example config/unicorn.rb
+
+**Important Note:**
+Make sure to edit both files to match your setup.
+
+## Configure GitLab DB settings
+
+ # Mysql
sudo -u gitlab cp config/database.yml.mysql config/database.yml
- # Copy unicorn config
- #
- sudo -u gitlab cp config/unicorn.rb.example config/unicorn.rb
+ # PostgreSQL
+ sudo -u gitlab cp config/database.yml.postgresql config/database.yml
-#### Install gems
+Make sure to update username/password in config/database.yml.
+
+## Install Gems
cd /home/gitlab/gitlab
sudo gem install charlock_holmes --version '0.6.9'
- sudo gem install bundler
- sudo -u gitlab -H bundle install --without development test postgres --deployment
-#### Configure git client
+ # For mysql db
+ sudo -u gitlab -H bundle install --deployment --without development test postgres
-Gitlab needs to be able to commit and push changes to gitolite.
-Git requires a username and email in order to be able to do that.
+ # Or For postgres db
+ sudo -u gitlab -H bundle install --deployment --without development test mysql
+## Configure Git
+
+GitLab needs to be able to commit and push changes to Gitolite. In order to do
+that Git requires a username and email. (We recommend using the same address
+used for the `email.from` setting in `config/gitlab.yml`)
+
+ sudo -u gitlab -H git config --global user.name "GitLab"
sudo -u gitlab -H git config --global user.email "gitlab@localhost"
- sudo -u gitlab -H git config --global user.name "Gitlab"
-#### Setup application
-
- 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/.gitolite/hooks/common/post-receive
sudo chown git:git /home/git/.gitolite/hooks/common/post-receive
-#### Check application status
+## Initialise Database and Activate Advanced Features
-Checking status:
-
- sudo -u gitlab bundle exec rake gitlab:app:status RAILS_ENV=production
+ sudo -u gitlab -H bundle exec rake gitlab:app:setup RAILS_ENV=production
- # OUTPUT EXAMPLE
- Starting diagnostic
- config/database.yml............exists
- config/gitlab.yml............exists
- /home/git/repositories/............exists
- /home/git/repositories/ is writable?............YES
- remote: Counting objects: 603, done.
- remote: Compressing objects: 100% (466/466), done.
- remote: Total 603 (delta 174), reused 0 (delta 0)
- Receiving objects: 100% (603/603), 53.29 KiB, done.
- Resolving deltas: 100% (174/174), done.
- Can clone gitolite-admin?............YES
- UMASK for .gitolite.rc is 0007? ............YES
- /home/git/share/gitolite/hooks/common/post-receive exists? ............YES
+## Check Application Status
-If you got all YES - congratulations! You can run a GitLab app.
+Check if GitLab and its environment is configured correctly:
-#### init script
+ sudo -u gitlab -H bundle exec rake gitlab:env:info RAILS_ENV=production
-Create init script in /etc/init.d/gitlab:
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u gitlab -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If you are all green: congratulations, you successfully installed GitLab!
+Although this is the case, there are still a few steps to go.
+
+
+## Install Init Script
+
+Download the init script (will be /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
-GitLab autostart:
+Make GitLab start on boot:
sudo update-rc.d gitlab defaults 21
-#### Now you should start GitLab application:
+
+Start your GitLab instance:
sudo service gitlab start
+ # or
+ sudo /etc/init.d/gitlab restart
# 7. Nginx
- # Install first
+**Note:**
+If you can't or don't want to use Nginx as your web server, have a look at the
+"Advanced Setup Tips" section.
+
+## Installation
sudo apt-get install nginx
- # Add GitLab to nginx sites & change with your host specific settings
+## Site Configuration
+
+Download an example site config:
+
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
+Make sure to edit the config file to match your setup:
+
# Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN**
# to the IP address and fully-qualified domain name
- # of the host serving GitLab.
+ # of your host serving GitLab
sudo vim /etc/nginx/sites-enabled/gitlab
- # Restart nginx:
+## Restart
+
sudo /etc/init.d/nginx restart
-# Done! Visit YOUR_SERVER for gitlab instance
+# Done!
-You can login via web using admin generated with setup:
+Visit YOUR_SERVER for your first GitLab login.
+The setup has created an admin account for you. You can use it to log in:
admin@local.host
5iveL!fe
+**Important Note:**
+Please go over to your profile page and immediately chage the password, so
+nobody can access your GitLab by using this login information later on.
+
+**Enjoy!**
+
- - -
-# Advanced setup tips:
+# Advanced Setup Tips
-_Checkout databases.md for PostgreSQL_
-
-## Customizing Resque's Redis connection
+## Custom Redis Connection
If you'd like Resque to connect to a Redis server on a non-standard port or on
-a different host, you can configure its connection string in the
-**config/resque.yml** file:
+a different host, you can configure its connection string via the
+`config/resque.yml` file.
- production: redis.example.com:6379
+ # example
+ production: redis.example.tld:6379
-**Ok - we have a working application now. **
-**But keep going - there are some things that should be done **
+
+## User-contributed Configurations
+
+You can find things like AWS installation scripts, init scripts or config files
+for alternative web server in our [recipes collection](https://github.com/gitlabhq/gitlab-recipes/).
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 75b02d64..ec5b013c 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -1,28 +1,56 @@
-## Platform requirements:
+# Hardware
-**The project is designed for the Linux operating system.**
+We recommend you to run GitLab on a server with at least 1GB RAM.
-It may work on FreeBSD and Mac OS, but we don't test our application for these systems and can't guarantee stability and full functionality.
+The necessary hard disk space largely depends on the size of the repos you want
+to use GitLab with. But as a *rule of thumb* you should have at least as much
+free space as your all repos combined take up.
-We officially support (recent versions of) these Linux distributions:
+
+
+# Operating Systems
+
+## Linux
+
+GitLab is developed for the Linux operating system.
+
+GitLab officially supports (recent versions of) these Linux distributions:
- Ubuntu Linux
- Debian/GNU Linux
-It should work on:
+It should also work on (though they are not officially supported):
+- Arch
+- CentOS
- Fedora
-- CentOs
+- Gentoo
- RedHat
-You might have some luck using these, but no guarantees:
+## Other Unix Systems
-- FreeBSD will likely work, see https://github.com/gitlabhq/gitlabhq/issues/796
-- MacOS X will likely work, see https://groups.google.com/forum/#!topic/gitlabhq/5IXHbPkjKLA
+There is nothing that prevents GitLab from running on other Unix operating
+systems. This means you may get it to work on systems running FreeBSD or OS X.
+**If you want to try, please proceed with caution!**
-GitLab does **not** run on Windows and we have no plans of making GitLab compatible.
+## Windows
+
+GitLab does **not** run on Windows and we have no plans of supporting it in the
+near future.
-## Hardware:
-We recommend to use server with at least 1GB RAM for gitlab instance.
+# Rubies
+
+GitLab requires Ruby (MRI) 1.9.3 and several Gems with native components.
+While it is generally possible to use other Rubies (like
+[JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/)) it might require
+some work on your part.
+
+
+
+# Installation troubles and reporting success or failure
+
+If you have troubles installing GitLab following the official installation guide
+or want to share your experience installing GitLab on a not officially supported
+platform, please follow the the contribution guide (see CONTRIBUTING.md).
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 575467b4..bbfeeb71 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -4,7 +4,7 @@ Creates a backup archive of the database and all repositories. This archive will
The filename will be `[TIMESTAMP]_gitlab_backup.tar`. This timestamp can be used to restore an specific backup.
```
-bundle exec rake gitlab:app:backup_create
+bundle exec rake gitlab:backup:create
```
Example output:
@@ -40,7 +40,7 @@ Deleting old backups... [SKIPPING]
### Restore a previously created backup
```
-bundle exec rake gitlab:app:backup_restore
+bundle exec rake gitlab:backup:restore
```
Options:
diff --git a/doc/raketasks/features.md b/doc/raketasks/features.md
index 2a66b1ca..7a2a4b66 100644
--- a/doc/raketasks/features.md
+++ b/doc/raketasks/features.md
@@ -1,6 +1,6 @@
### Enable usernames and namespaces for user projects
-This command will enable the namespace feature introduced in v4.0. It will move every project in its namespace folder.
+This command will enable the namespaces feature introduced in v4.0. It will move every project in its namespace folder.
Note:
@@ -13,7 +13,7 @@ Old path: `git@example.org:myrepo.git`
New path: `git@example.org:username/myrepo.git` or `git@example.org:groupname/myrepo.git`
```
-bundle exec rake gitlab:activate_namespaces
+bundle exec rake gitlab:enable_namespaces
```
@@ -22,7 +22,7 @@ bundle exec rake gitlab:activate_namespaces
This command will enable the auto merge feature. After this you will be able to **merge a merge request** via GitLab and use the **online editor**.
```
-bundle exec rake gitlab:app:enable_automerge
+bundle exec rake gitlab:enable_automerge
```
Example output:
diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md
index 7bbb6571..bb8e1ed2 100644
--- a/doc/raketasks/maintenance.md
+++ b/doc/raketasks/maintenance.md
@@ -11,32 +11,141 @@ bundle exec rake gitlab:app:setup
```
-### Check GitLab installation status
+### Gather information about GitLab and the system it runs on
-[Trouble-Shooting-Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide)
+This command gathers information about your GitLab installation and the System
+it runs on. These may be useful when asking for help or reporting issues.
```
-bundle exec rake gitlab:app:status
+bundle exec rake gitlab:env:info
```
Example output:
```
-config/database.yml............exists
-config/gitlab.yml............exists
-/home/git/repositories/............exists
-/home/git/repositories/ is writable?............YES
-Can clone gitolite-admin?............YES
-Can git commit?............YES
-UMASK for .gitolite.rc is 0007? ............YES
-/home/git/.gitolite/hooks/common/post-receive exists? ............YES
+System information
+System: Debian 6.0.6
+Current User: gitlab
+Using RVM: yes
+RVM Version: 1.17.2
+Ruby Version: ruby-1.9.3-p327
+Gem Version: 1.8.24
+Bundler Version:1.2.3
+Rake Version: 10.0.1
-Validating projects repositories:
-* abcd.....post-receive file ok
-* abcdtest.....post-receive file missing
+GitLab information
+Version: 3.1.0
+Resivion: fd5141d
+Directory: /home/gitlab/gitlab
+DB Adapter: mysql2
+URL: http://localhost:3000
+HTTP Clone URL: http://localhost:3000/some-project.git
+SSH Clone URL: git@localhost:some-project.git
+Using LDAP: no
+Using Omniauth: no
-Finished
+Gitolite information
+Version: v3.04-4-g4524f01
+Admin URI: git@localhost:gitolite-admin
+Admin Key: gitlab
+Repositories: /home/git/repositories/
+Hooks: /home/git/.gitolite/hooks/
+Git: /usr/bin/git
+```
+
+### Check GitLab configuration
+
+Runs the following rake tasks:
+
+* gitlab:env:check
+* gitlab:gitolite:check
+* gitlab:resque:check
+* gitlab:app:check
+
+It will check that each component was setup according to the installation guide and suggest fixes for issues found.
+
+You may also have a look at our [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide).
+
+```
+bundle exec rake gitlab:check
+```
+
+Example output:
+
+```
+Checking Environment ...
+
+gitlab user is in git group? ... yes
+Has no "-e" in ~git/.profile ... yes
+Git configured for gitlab user? ... yes
+Has python2? ... yes
+python2 is supported version? ... yes
+
+Checking Environment ... Finished
+
+Checking Gitolite ...
+
+Using recommended version ... yes
+Repo umask is 0007 in .gitolite.rc? ... yes
+Allow all Git config keys in .gitolite.rc ... yes
+Config directory exists? ... yes
+Config directory owned by git:git? ... yes
+Config directory access is drwxr-x---? ... yes
+Repo base directory exists? ... yes
+Repo base owned by git:git? ... yes
+Repo base access is drwsrws---? ... yes
+Can clone gitolite-admin? ... yes
+Can commit to gitolite-admin? ... yes
+post-receive hook exists? ... yes
+post-receive hook up-to-date? ... yes
+post-receive hooks in repos are links: ...
+GitLab ... ok
+Non-Ascii Files Test ... ok
+Touch Commit Test ... ok
+Without Master Test ... ok
+Git config in repos: ...
+GitLab ... ok
+Non-Ascii Files Test ... ok
+Touch Commit Test ... ok
+Without Master Test ... ok
+
+Checking Gitolite ... Finished
+
+Checking Resque ...
+
+Running? ... yes
+
+Checking Resque ... Finished
+
+Checking GitLab ...
+
+Database config exists? ... yes
+Database is not SQLite ... yes
+All migrations up? ... yes
+GitLab config exists? ... yes
+GitLab config not outdated? ... yes
+Log directory writable? ... yes
+Tmp directory writable? ... yes
+Init script exists? ... yes
+Init script up-to-date? ... yes
+Projects have satellites? ...
+GitLab ... yes
+Non-Ascii Files Test ... yes
+Touch Commit Test ... yes
+Without Master Test ... yes
+
+Checking GitLab ... Finished
+```
+
+
+### (Re-)Create satellite repos
+
+This will create satellite repos for all your projects.
+If necessary, remove the `tmp/repo_satellites` directory and rerun the command below.
+
+```
+bundle exec rake gitlab:satellites:create
```
diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md
index e4ca5280..021ce359 100644
--- a/doc/raketasks/user_management.md
+++ b/doc/raketasks/user_management.md
@@ -1,7 +1,7 @@
### Add user to as a developer to all projects
```
-bundle exec rake add_user_to_project_teams[username@domain.tld]
+bundle exec rake gitlab:import:user_to_projects[username@domain.tld]
```
@@ -12,5 +12,5 @@ Notes:
* admin users are added as masters
```
-bundle exec rake add_users_to_project_teams
+bundle exec rake gitlab:import:all_users_to_all_projects
```
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 99529373..d6ef384c 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -24,11 +24,9 @@ Feature: Project Issues
Given I click link "Release 0.4"
Then I should see issue "Release 0.4"
- @javascript
Scenario: I submit new unassigned issue
Given I click link "New Issue"
And I submit new issue "500 error on profile"
- Given I click link "500 error on profile"
Then I should see issue "500 error on profile"
@javascript
@@ -57,26 +55,19 @@ Feature: Project Issues
Then I should see "Release 0.3" in issues
And I should not see "Release 0.4" in issues
- # TODO: find out solution for poltergeist/phantomjs or remove
- # @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
+ # Disable this two cause of random failing
+ # TODO: fix after v4.0 released
+ #@javascript
+ #Scenario: I create Issue with pre-selected milestone
+ #Given project "Shop" has milestone "v2.2"
+ #And project "Shop" has milestone "v3.0"
+ #And I visit project "Shop" issues page
+ #When I select milestone "v3.0"
+ #And I click link "New Issue"
+ #Then I should see selected milestone with title "v3.0"
- @javascript
- Scenario: I create Issue with pre-selected milestone
- Given project "Shop" has milestone "v2.2"
- And project "Shop" has milestone "v3.0"
- And I visit project "Shop" issues page
- When I select milestone "v3.0"
- And I click link "New Issue"
- Then I should see selected milestone with title "v3.0"
-
- @javascript
- Scenario: I create Issue with pre-selected assignee
- When I select first assignee from "Shop" project
- And I click link "New Issue"
- Then I should see first assignee from "Shop" as selected assignee
+ #@javascript
+ #Scenario: I create Issue with pre-selected assignee
+ #When I select first assignee from "Shop" project
+ #And I click link "New Issue"
+ #Then I should see first assignee from "Shop" as selected assignee
diff --git a/features/steps/project/project_browse_commits.rb b/features/steps/project/project_browse_commits.rb
index 6bf164e2..2c03ce14 100644
--- a/features/steps/project/project_browse_commits.rb
+++ b/features/steps/project/project_browse_commits.rb
@@ -32,8 +32,8 @@ class ProjectBrowseCommits < Spinach::FeatureSteps
end
And 'I fill compare fields with refs' do
- fill_in "from", with: "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
- fill_in "to", with: "8716fc78f3c65bbf7bcf7b574febd583bc5d2812"
+ fill_in "from", with: "8716fc78f3c65bbf7bcf7b574febd583bc5d2812"
+ fill_in "to", with: "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
click_button "Compare"
end
diff --git a/features/steps/project/project_issues.rb b/features/steps/project/project_issues.rb
index cc0acb5b..2103aeb1 100644
--- a/features/steps/project/project_issues.rb
+++ b/features/steps/project/project_issues.rb
@@ -95,7 +95,7 @@ class ProjectIssues < Spinach::FeatureSteps
end
Then 'I should see selected milestone with title "v3.0"' do
- issues_milestone_selector = "#milestone_id_chzn > a"
+ issues_milestone_selector = "#issue_milestone_id_chzn > a"
page.find(issues_milestone_selector).should have_content("v3.0")
end
@@ -106,7 +106,7 @@ class ProjectIssues < Spinach::FeatureSteps
end
Then 'I should see first assignee from "Shop" as selected assignee' do
- issues_assignee_selector = "#assignee_id_chzn > a"
+ issues_assignee_selector = "#issue_assignee_id_chzn > a"
project = Project.find_by_name "Shop"
assignee_name = project.users.first.name
page.find(issues_assignee_selector).should have_content(assignee_name)
diff --git a/features/support/env.rb b/features/support/env.rb
index a30b3577..500de0f3 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -33,11 +33,9 @@ DatabaseCleaner.strategy = :truncation
Spinach.hooks.before_scenario do
# Use tmp dir for FS manipulations
- Gitlab.config.stub(git_base_path: Rails.root.join('tmp', 'test-git-base-path'))
- FileUtils.rm_rf Gitlab.config.git_base_path
- FileUtils.mkdir_p Gitlab.config.git_base_path
-
- DatabaseCleaner.start
+ Gitlab.config.gitolite.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
+ FileUtils.rm_rf Gitlab.config.gitolite.repos_path
+ FileUtils.mkdir_p Gitlab.config.gitolite.repos_path
end
Spinach.hooks.after_scenario do
diff --git a/lib/api.rb b/lib/api.rb
index d01d534c..f58b82ff 100644
--- a/lib/api.rb
+++ b/lib/api.rb
@@ -2,7 +2,7 @@ Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file}
module Gitlab
class API < Grape::API
- version 'v2', using: :path
+ version 'v3', using: :path
rescue_from ActiveRecord::RecordNotFound do
rack_response({'message' => '404 Not found'}.to_json, 404)
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9e9d4459..e5b2685a 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1,12 +1,12 @@
module Gitlab
module Entities
class User < Grape::Entity
- expose :id, :email, :name, :bio, :skype, :linkedin, :twitter,
+ expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter,
:dark_scheme, :theme_id, :blocked, :created_at
end
class UserBasic < Grape::Entity
- expose :id, :email, :name, :blocked, :created_at
+ expose :id, :username, :email, :name, :blocked, :created_at
end
class UserLogin < UserBasic
@@ -18,7 +18,7 @@ module Gitlab
end
class Project < Grape::Entity
- expose :id, :code, :name, :description, :path, :default_branch
+ expose :id, :name, :description, :default_branch
expose :owner, using: Entities::UserBasic
expose :private_flag, as: :private
expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index e9305b40..6bd8111c 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -5,13 +5,18 @@ module Gitlab
end
def user_project
- if @project ||= current_user.projects.find_by_id(params[:id]) ||
- current_user.projects.find_by_path(params[:id])
- else
- not_found!
- end
+ @project ||= find_project
+ @project || not_found!
+ end
- @project
+ def find_project
+ project = Project.find_by_id(params[:id]) || Project.find_with_namespace(params[:id])
+
+ if project && can?(current_user, :read_project, project)
+ project
+ else
+ nil
+ end
end
def paginate(object)
@@ -32,6 +37,10 @@ module Gitlab
end
end
+ def can?(object, action, subject)
+ abilities.allowed?(object, action, subject)
+ end
+
def attributes_for_keys(keys)
attrs = {}
keys.each do |key|
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 4ee2d11f..3be55881 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -17,7 +17,7 @@ module Gitlab
# Get a list of project issues
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id/issues
get ":id/issues" do
@@ -27,7 +27,7 @@ module Gitlab
# Get a single project issue
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# issue_id (required) - The ID of a project issue
# Example Request:
# GET /projects/:id/issues/:issue_id
@@ -39,7 +39,7 @@ module Gitlab
# Create a new project issue
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# title (required) - The title of an issue
# description (optional) - The description of an issue
# assignee_id (optional) - The ID of a user to assign issue
@@ -62,7 +62,7 @@ module Gitlab
# Update an existing issue
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# issue_id (required) - The ID of a project issue
# title (optional) - The title of an issue
# description (optional) - The description of an issue
@@ -88,7 +88,7 @@ module Gitlab
# Delete a project issue (deprecated)
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# issue_id (required) - The ID of a project issue
# Example Request:
# DELETE /projects/:id/issues/:issue_id
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 1fa0c549..470cd1e1 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -8,7 +8,7 @@ module Gitlab
# List merge requests
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
#
# Example:
# GET /projects/:id/merge_requests
@@ -22,7 +22,7 @@ module Gitlab
# Show MR
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
@@ -40,7 +40,7 @@ module Gitlab
#
# Parameters:
#
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# source_branch (required) - The source branch
# target_branch (required) - The target branch
# assignee_id - Assignee user ID
@@ -67,7 +67,7 @@ module Gitlab
# Update MR
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# source_branch - The source branch
# target_branch - The target branch
@@ -95,7 +95,7 @@ module Gitlab
# Post comment to merge request
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# note (required) - Text of comment
# Examples:
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index f55dfd04..6aca9d01 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -7,7 +7,7 @@ module Gitlab
# Get a list of project milestones
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id/milestones
get ":id/milestones" do
@@ -19,7 +19,7 @@ module Gitlab
# Get a single project milestone
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# milestone_id (required) - The ID of a project milestone
# Example Request:
# GET /projects/:id/milestones/:milestone_id
@@ -33,7 +33,7 @@ module Gitlab
# Create a new project milestone
#
# Parameters:
- # id (required) - The ID or code name of the project
+ # id (required) - The ID of the project
# title (required) - The title of the milestone
# description (optional) - The description of the milestone
# due_date (optional) - The due date of the milestone
@@ -54,7 +54,7 @@ module Gitlab
# Update an existing project milestone
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# milestone_id (required) - The ID of a project milestone
# title (optional) - The title of a milestone
# description (optional) - The description of a milestone
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index a3e18584..4875ac4c 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -9,7 +9,7 @@ module Gitlab
# Get a list of project wall notes
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id/notes
get ":id/notes" do
@@ -20,7 +20,7 @@ module Gitlab
# Get a single project wall note
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# note_id (required) - The ID of a note
# Example Request:
# GET /projects/:id/notes/:note_id
@@ -32,7 +32,7 @@ module Gitlab
# Create a new project wall note
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# body (required) - The content of a note
# Example Request:
# POST /projects/:id/notes
@@ -54,7 +54,7 @@ module Gitlab
# Get a list of project +noteable+ notes
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# noteable_id (required) - The ID of an issue or snippet
# Example Request:
# GET /projects/:id/issues/:noteable_id/notes
@@ -67,7 +67,7 @@ module Gitlab
# Get a single +noteable+ note
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# noteable_id (required) - The ID of an issue or snippet
# note_id (required) - The ID of a note
# Example Request:
@@ -82,7 +82,7 @@ module Gitlab
# Create a new +noteable+ note
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# noteable_id (required) - The ID of an issue or snippet
# body (required) - The content of a note
# Example Request:
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 384dbcd5..fb01524d 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -16,7 +16,7 @@ module Gitlab
# Get a single project
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id
get ":id" do
@@ -27,8 +27,6 @@ module Gitlab
#
# Parameters:
# name (required) - name for new project
- # code (optional) - code 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
@@ -56,18 +54,23 @@ module Gitlab
# Get a project team members
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
+ # query - Query string
# Example Request:
# GET /projects/:id/members
get ":id/members" do
- @members = paginate user_project.users
+ if params[:query].present?
+ @members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%")
+ else
+ @members = paginate user_project.users
+ end
present @members, with: Entities::ProjectMember, project: user_project
end
# Get a project team members
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# user_id (required) - The ID of a user
# Example Request:
# GET /projects/:id/members/:user_id
@@ -79,7 +82,7 @@ module Gitlab
# Add a new project team member
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# user_id (required) - The ID of a user
# access_level (required) - Project access level
# Example Request:
@@ -102,7 +105,7 @@ module Gitlab
# Update project team member
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# user_id (required) - The ID of a team member
# access_level (required) - Project access level
# Example Request:
@@ -122,7 +125,7 @@ module Gitlab
# Remove a team member from project
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# user_id (required) - The ID of a team member
# Example Request:
# DELETE /projects/:id/members/:user_id
@@ -135,7 +138,7 @@ module Gitlab
# Get project hooks
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id/hooks
get ":id/hooks" do
@@ -147,7 +150,7 @@ module Gitlab
# Get a project hook
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# hook_id (required) - The ID of a project hook
# Example Request:
# GET /projects/:id/hooks/:hook_id
@@ -160,7 +163,7 @@ module Gitlab
# Add hook to project
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# url (required) - The hook URL
# Example Request:
# POST /projects/:id/hooks
@@ -177,7 +180,7 @@ module Gitlab
# Update an existing project hook
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# hook_id (required) - The ID of a project hook
# url (required) - The hook URL
# Example Request:
@@ -198,7 +201,7 @@ module Gitlab
# Delete project hook
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# hook_id (required) - The ID of hook to delete
# Example Request:
# DELETE /projects/:id/hooks
@@ -211,7 +214,7 @@ module Gitlab
# Get a project repository branches
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id/repository/branches
get ":id/repository/branches" do
@@ -221,7 +224,7 @@ module Gitlab
# Get a single branch
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# branch (required) - The name of the branch
# Example Request:
# GET /projects/:id/repository/branches/:branch
@@ -233,7 +236,7 @@ module Gitlab
# Get a project repository tags
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id/repository/tags
get ":id/repository/tags" do
@@ -243,7 +246,7 @@ module Gitlab
# Get a project repository commits
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# ref_name (optional) - The name of a repository branch or tag
# Example Request:
# GET /projects/:id/repository/commits
@@ -261,7 +264,7 @@ module Gitlab
# Get a project snippets
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# Example Request:
# GET /projects/:id/snippets
get ":id/snippets" do
@@ -271,7 +274,7 @@ module Gitlab
# Get a project snippet
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# snippet_id (required) - The ID of a project snippet
# Example Request:
# GET /projects/:id/snippets/:snippet_id
@@ -283,7 +286,7 @@ module Gitlab
# Create a new project snippet
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# title (required) - The title of a snippet
# file_name (required) - The name of a snippet file
# lifetime (optional) - The expiration date of a snippet
@@ -309,7 +312,7 @@ module Gitlab
# Update an existing project snippet
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# snippet_id (required) - The ID of a project snippet
# title (optional) - The title of a snippet
# file_name (optional) - The name of a snippet file
@@ -335,7 +338,7 @@ module Gitlab
# Delete a project snippet
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# snippet_id (required) - The ID of a project snippet
# Example Request:
# DELETE /projects/:id/snippets/:snippet_id
@@ -349,7 +352,7 @@ module Gitlab
# Get a raw project snippet
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# snippet_id (required) - The ID of a project snippet
# Example Request:
# GET /projects/:id/snippets/:snippet_id/raw
@@ -362,7 +365,7 @@ module Gitlab
# Get a raw file contents
#
# Parameters:
- # id (required) - The ID or code name of a project
+ # id (required) - The ID of a project
# sha (required) - The commit or branch name
# filepath (required) - The path to the file to display
# Example Request:
diff --git a/lib/api/users.rb b/lib/api/users.rb
index cad99fd9..140c20f6 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -101,8 +101,6 @@ module Gitlab
key = current_user.keys.find params[:id]
key.delete
end
-
-
end
end
end
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 056fb034..8c45c935 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -38,7 +38,7 @@ module Gitlab
email: email,
password: password,
password_confirmation: password,
- projects_limit: Gitlab.config.default_projects_limit,
+ projects_limit: Gitlab.config.gitlab.default_projects_limit,
}, as: :admin)
if Gitlab.config.omniauth['block_auto_created_users'] && !ldap
@user.blocked = true
diff --git a/lib/gitlab/backend/gitolite.rb b/lib/gitlab/backend/gitolite.rb
index 7c3861bd..3b8a2090 100644
--- a/lib/gitlab/backend/gitolite.rb
+++ b/lib/gitlab/backend/gitolite.rb
@@ -38,7 +38,7 @@ module Gitlab
end
def url_to_repo path
- Gitlab.config.ssh_path + "#{path}.git"
+ Gitlab.config.gitolite.ssh_path_prefix + "#{path}.git"
end
def enable_automerge
diff --git a/lib/gitlab/backend/gitolite_config.rb b/lib/gitlab/backend/gitolite_config.rb
index 70ccc478..a2bc4ca8 100644
--- a/lib/gitlab/backend/gitolite_config.rb
+++ b/lib/gitlab/backend/gitolite_config.rb
@@ -16,7 +16,7 @@ module Gitlab
def ga_repo
@ga_repo ||= ::Gitolite::GitoliteAdmin.new(
File.join(config_tmp_dir,'gitolite'),
- conf: Gitlab.config.gitolite_config_file
+ conf: Gitlab.config.gitolite.config_file
)
end
@@ -167,7 +167,7 @@ module Gitlab
# Enable access to all repos for gitolite admin.
# We use it for accept merge request feature
def admin_all_repo
- owner_name = Gitlab.config.gitolite_admin_key
+ owner_name = Gitlab.config.gitolite.admin_key
# @ALL repos premission for gitolite owner
repo_name = "@all"
@@ -189,7 +189,7 @@ module Gitlab
def pull tmp_dir
Dir.mkdir tmp_dir
- `git clone #{Gitlab.config.gitolite_admin_uri} #{tmp_dir}/gitolite`
+ `git clone #{Gitlab.config.gitolite.admin_uri} #{tmp_dir}/gitolite`
unless File.exists?(File.join(tmp_dir, 'gitolite', 'conf', 'gitolite.conf'))
raise PullError, "unable to clone gitolite-admin repo"
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 9fafc961..7c31117f 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -38,12 +38,12 @@ module Grack
end
def validate_get_request
- true
+ can?(user, :download_code, project)
end
def validate_post_request
if @request.path_info.end_with?('git-upload-pack')
- can?(user, :push_code, project)
+ can?(user, :download_code, project)
elsif @request.path_info.end_with?('git-receive-pack')
action = if project.protected_branch?(current_ref)
:push_code_to_protected_branches
diff --git a/lib/gitlab/graph/commit.rb b/lib/gitlab/graph/commit.rb
index af8d7828..3d82c344 100644
--- a/lib/gitlab/graph/commit.rb
+++ b/lib/gitlab/graph/commit.rb
@@ -28,7 +28,7 @@ module Gitlab
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha
h[:date] = date
- h[:message] = escape_once(message)
+ h[:message] = message
h[:login] = author.email
h
end
diff --git a/lib/gitlab/graph/json_builder.rb b/lib/gitlab/graph/json_builder.rb
index c2c3fa66..a5914363 100644
--- a/lib/gitlab/graph/json_builder.rb
+++ b/lib/gitlab/graph/json_builder.rb
@@ -17,16 +17,15 @@ module Gitlab
@commits = collect_commits
@days = index_commits
end
-
- def days_json
- @days_json = @days.compact.map { |d| [d.day, d.strftime("%b")] }.to_json
+
+ def to_json(*args)
+ {
+ days: @days.compact.map { |d| [d.day, d.strftime("%b")] },
+ commits: @commits.map(&:to_graph_hash)
+ }.to_json(*args)
end
-
- def commits_json
- @commits_json = @commits.map(&:to_graph_hash).to_json
- end
-
- protected
+
+ protected
# Get commits from repository
#
diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb
index 8b4eee5d..389eef33 100644
--- a/lib/gitlab/logger.rb
+++ b/lib/gitlab/logger.rb
@@ -14,6 +14,11 @@ module Gitlab
logs = `tail -n 2000 #{path}`.split("\n")
end
+ def self.read_latest_for filename
+ path = Rails.root.join("log", filename)
+ logs = `tail -n 2000 #{path}`.split("\n")
+ end
+
def self.build
new(Rails.root.join("log", file_name))
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 23f408c6..c947e69a 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -81,28 +81,32 @@ module Gitlab
end
REFERENCE_PATTERN = %r{
- (\W)? # Prefix (1)
- ( # Reference (2)
- @([\w\._]+) # User name (3)
- |[#!$](\d+) # Issue/MR/Snippet ID (4)
- |([\h]{6,40}) # Commit ID (5)
+ (?\W)? # Prefix
+ ( # Reference
+ @(?[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name
+ |\#(?\d+) # Issue ID
+ |!(?\d+) # MR ID
+ |\$(?\d+) # Snippet ID
+ |(?[\h]{6,40}) # Commit ID
)
- (\W)? # Suffix (6)
+ (?\W)? # Suffix
}x.freeze
+ TYPES = [:user, :issue, :merge_request, :snippet, :commit].freeze
+
def parse_references(text)
# parse reference links
text.gsub!(REFERENCE_PATTERN) do |match|
- prefix = $1 || ''
- reference = $2
- identifier = $3 || $4 || $5
- suffix = $6 || ''
+ prefix = $~[:prefix]
+ suffix = $~[:suffix]
+ type = TYPES.select{|t| !$~[t].nil?}.first
+ identifier = $~[type]
# Avoid HTML entities
- if prefix.ends_with?('&') || suffix.starts_with?(';')
+ if prefix && suffix && prefix[0] == '&' && suffix[-1] == ';'
match
- elsif ref_link = reference_link(reference, identifier)
- prefix + ref_link + suffix
+ elsif ref_link = reference_link(type, identifier)
+ "#{prefix}#{ref_link}#{suffix}"
else
match
end
@@ -115,7 +119,7 @@ module Gitlab
# parse emoji
text.gsub!(EMOJI_PATTERN) do |match|
if valid_emoji?($2)
- image_tag("emoji/#{$2}.png", class: 'emoji', title: $1, alt: $1)
+ image_tag("emoji/#{$2}.png", class: 'emoji', title: $1, alt: $1, size: "20x20")
else
match
end
@@ -137,19 +141,12 @@ module Gitlab
# identifier - Object identifier (Issue ID, SHA hash, etc.)
#
# Returns string rendered by the processing method
- def reference_link(reference, identifier)
- case reference
- when /^@/ then reference_user(identifier)
- when /^#/ then reference_issue(identifier)
- when /^!/ then reference_merge_request(identifier)
- when /^\$/ then reference_snippet(identifier)
- when /^\h/ then reference_commit(identifier)
- end
+ def reference_link(type, identifier)
+ send("reference_#{type}", identifier)
end
def reference_user(identifier)
- if user = @project.users.where(name: identifier).first
- member = @project.users_projects.where(user_id: user).first
+ if member = @project.users_projects.joins(:user).where(users: { username: identifier }).first
link_to("@#{identifier}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member
end
end
diff --git a/lib/gitlab/project_mover.rb b/lib/gitlab/project_mover.rb
index eeab22ae..def6e900 100644
--- a/lib/gitlab/project_mover.rb
+++ b/lib/gitlab/project_mover.rb
@@ -15,10 +15,10 @@ module Gitlab
def execute
# Create new dir if missing
- new_dir_path = File.join(Gitlab.config.git_base_path, new_dir)
+ new_dir_path = File.join(Gitlab.config.gitolite.repos_path, new_dir)
system("mkdir -m 770 #{new_dir_path}") unless File.exists?(new_dir_path)
- old_path = File.join(Gitlab.config.git_base_path, old_dir, "#{project.path}.git")
+ old_path = File.join(Gitlab.config.gitolite.repos_path, old_dir, "#{project.path}.git")
new_path = File.join(new_dir_path, "#{project.path}.git")
if File.exists? new_path
diff --git a/lib/hooks/post-receive b/lib/hooks/post-receive
index 4a3ce372..ebd9e1a0 100755
--- a/lib/hooks/post-receive
+++ b/lib/hooks/post-receive
@@ -6,7 +6,6 @@
while read oldrev newrev ref
do
# For every branch or tag that was pushed, create a Resque job in redis.
- pwd=`pwd`
- reponame=`basename "$pwd" | sed s/\.git$//`
- env -i redis-cli rpush "resque:gitlab:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$reponame\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1
+ repo_path=`pwd`
+ env -i redis-cli rpush "resque:gitlab:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$repo_path\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1
done
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index bd590f92..3a430e0b 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -11,14 +11,20 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
def block_code(code, language)
options = { options: {encoding: 'utf-8'} }
+ options.merge!(lexer: language.downcase) if Pygments::Lexer.find(language)
- h.content_tag :div, class: h.user_color_scheme_class do
- if Pygments::Lexer.find(language)
- Pygments.highlight(code, options.merge(lexer: language.downcase))
- else
- Pygments.highlight(code, options)
- end.html_safe
- end
+ # New lines are placed to fix an rendering issue
+ # with code wrapped inside tag for next case:
+ #
+ # # Title kinda h1
+ #
+ # ruby code here
+ #
+ <<-HTML
+
+
#{Pygments.highlight(code, options)}
+
+ HTML
end
def postprocess(full_document)
diff --git a/lib/tasks/bulk_add_permission.rake b/lib/tasks/bulk_add_permission.rake
deleted file mode 100644
index bf08ace8..00000000
--- a/lib/tasks/bulk_add_permission.rake
+++ /dev/null
@@ -1,20 +0,0 @@
-desc "Add all users to all projects (admin users are added as masters)"
-task :add_users_to_project_teams => :environment do |t, args|
- user_ids = User.where(:admin => false).pluck(:id)
- admin_ids = User.where(:admin => true).pluck(:id)
-
- Project.find_each do |project|
- puts "Importing #{user_ids.size} users into #{project.code}"
- UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER)
- puts "Importing #{admin_ids.size} admins into #{project.code}"
- UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER)
- end
-end
-
-desc "Add user to as a developer to all projects"
-task :add_user_to_project_teams, [:email] => :environment do |t, args|
- user = User.find_by_email args.email
- project_ids = Project.pluck(:id)
-
- UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER)
-end
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index c01fe479..44da6d67 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -1,14 +1,14 @@
require 'active_record/fixtures'
namespace :gitlab do
- namespace :app do
+ namespace :backup do
# Create backup of GitLab system
desc "GITLAB | Create a backup of the GitLab system"
- task :backup_create => :environment do
- Rake::Task["gitlab:app:db_dump"].invoke
- Rake::Task["gitlab:app:repo_dump"].invoke
+ task :create => :environment do
+ Rake::Task["gitlab:backup:db:create"].invoke
+ Rake::Task["gitlab:backup:repo:create"].invoke
- Dir.chdir(Gitlab.config.backup_path)
+ Dir.chdir(Gitlab.config.backup.path)
# saving additional informations
s = {}
@@ -17,7 +17,7 @@ namespace :gitlab do
s[:gitlab_version] = %x{git rev-parse HEAD}.gsub(/\n/,"")
s[:tar_version] = %x{tar --version | head -1}.gsub(/\n/,"")
- File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file|
+ File.open("#{Gitlab.config.backup.path}/backup_information.yml", "w+") do |file|
file << s.to_yaml.gsub(/^---\n/,'')
end
@@ -39,10 +39,10 @@ namespace :gitlab do
# delete backups
print "Deleting old backups... "
- if Gitlab.config.backup_keep_time > 0
+ if Gitlab.config.backup.keep_time > 0
file_list = Dir.glob("*_gitlab_backup.tar").map { |f| f.split(/_/).first.to_i }
file_list.sort.each do |timestamp|
- if Time.at(timestamp) < (Time.now - Gitlab.config.backup_keep_time)
+ if Time.at(timestamp) < (Time.now - Gitlab.config.backup.keep_time)
%x{rm #{timestamp}_gitlab_backup.tar}
end
end
@@ -54,15 +54,15 @@ namespace :gitlab do
# Restore backup of GitLab system
desc "GITLAB | Restore a previously created backup"
- task :backup_restore => :environment do
- Dir.chdir(Gitlab.config.backup_path)
+ task :restore => :environment do
+ Dir.chdir(Gitlab.config.backup.path)
# check for existing backups in the backup dir
file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i }
puts "no backups found" if file_list.count == 0
if file_list.count > 1 && ENV["BACKUP"].nil?
puts "Found more than one backup, please specify which one you want to restore:"
- puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup"
+ puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup"
exit 1;
end
@@ -93,8 +93,8 @@ namespace :gitlab do
exit 1
end
- Rake::Task["gitlab:app:db_restore"].invoke
- Rake::Task["gitlab:app:repo_restore"].invoke
+ Rake::Task["gitlab:backup:db:restore"].invoke
+ Rake::Task["gitlab:backup:repo:restore"].invoke
# cleanup: remove tmp files
print "Deleting tmp directories..."
@@ -110,82 +110,86 @@ namespace :gitlab do
################################# REPOSITORIES #################################
- task :repo_dump => :environment do
- backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
- FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo)
- puts "Dumping repositories:"
- project = Project.all.map { |n| [n.path, n.path_to_repo] }
- project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
- project.each do |project|
- print "- Dumping repository #{project.first}... "
- if Kernel.system("cd #{project.second} > /dev/null 2>&1 && git bundle create #{backup_path_repo}/#{project.first}.bundle --all > /dev/null 2>&1")
- puts "[DONE]".green
- else
- puts "[FAILED]".red
+ namespace :repo do
+ task :create => :environment do
+ backup_path_repo = File.join(Gitlab.config.backup.path, "repositories")
+ FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo)
+ puts "Dumping repositories:"
+ project = Project.all.map { |n| [n.path, n.path_to_repo] }
+ project << ["gitolite-admin.git", File.join(Gitlab.config.git_base_path, "gitolite-admin.git")]
+ project.each do |project|
+ print "- Dumping repository #{project.first}... "
+ if Kernel.system("cd #{project.second} > /dev/null 2>&1 && git bundle create #{backup_path_repo}/#{project.first}.bundle --all > /dev/null 2>&1")
+ puts "[DONE]".green
+ else
+ puts "[FAILED]".red
+ end
end
end
- end
- task :repo_restore => :environment do
- backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
- puts "Restoring repositories:"
- project = Project.all.map { |n| [n.path, n.path_to_repo] }
- project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
- project.each do |project|
- print "- Restoring repository #{project.first}... "
- FileUtils.rm_rf(project.second) if File.dirname(project.second) # delete old stuff
- if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1")
- permission_commands = [
- "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}",
- "sudo chown -R #{Gitlab.config.ssh_user}:#{Gitlab.config.ssh_user} #{Gitlab.config.git_base_path}"
- ]
- permission_commands.each { |command| Kernel.system(command) }
- puts "[DONE]".green
- else
- puts "[FAILED]".red
+ task :restore => :environment do
+ backup_path_repo = File.join(Gitlab.config.backup.path, "repositories")
+ puts "Restoring repositories:"
+ project = Project.all.map { |n| [n.path, n.path_to_repo] }
+ project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
+ project.each do |project|
+ print "- Restoring repository #{project.first}... "
+ FileUtils.rm_rf(project.second) if File.dirname(project.second) # delete old stuff
+ if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1")
+ permission_commands = [
+ "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}",
+ "sudo chown -R #{Gitlab.config.ssh_user}:#{Gitlab.config.ssh_user} #{Gitlab.config.git_base_path}"
+ ]
+ permission_commands.each { |command| Kernel.system(command) }
+ puts "[DONE]".green
+ else
+ puts "[FAILED]".red
+ end
end
end
end
###################################### DB ######################################
- task :db_dump => :environment do
- backup_path_db = File.join(Gitlab.config.backup_path, "db")
- FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db)
+ namespace :db do
+ task :create => :environment do
+ backup_path_db = File.join(Gitlab.config.backup.path, "db")
+ FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db)
- puts "Dumping database tables:"
- ActiveRecord::Base.connection.tables.each do |tbl|
- print "- Dumping table #{tbl}... "
- count = 1
- File.open(File.join(backup_path_db, tbl + ".yml"), "w+") do |file|
- ActiveRecord::Base.connection.select_all("SELECT * FROM `#{tbl}`").each do |line|
- line.delete_if{|k,v| v.blank?}
- output = {tbl + '_' + count.to_s => line}
- file << output.to_yaml.gsub(/^---\n/,'') + "\n"
- count += 1
+ puts "Dumping database tables:"
+ ActiveRecord::Base.connection.tables.each do |tbl|
+ print "- Dumping table #{tbl}... "
+ count = 1
+ File.open(File.join(backup_path_db, tbl + ".yml"), "w+") do |file|
+ ActiveRecord::Base.connection.select_all("SELECT * FROM `#{tbl}`").each do |line|
+ line.delete_if{|k,v| v.blank?}
+ output = {tbl + '_' + count.to_s => line}
+ file << output.to_yaml.gsub(/^---\n/,'') + "\n"
+ count += 1
+ end
+ puts "[DONE]".green
+ end
+ end
+ end
+
+ task :restore=> :environment do
+ backup_path_db = File.join(Gitlab.config.backup.path, "db")
+
+ puts "Restoring database tables:"
+ Rake::Task["db:reset"].invoke
+
+ Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir|
+ fixture_file = File.basename(dir, ".*" )
+ print "- Loading fixture #{fixture_file}..."
+ if File.size(dir) > 0
+ ActiveRecord::Fixtures.create_fixtures(backup_path_db, fixture_file)
+ puts "[DONE]".green
+ else
+ puts "[SKIPPING]".yellow
end
- puts "[DONE]".green
end
end
end
- task :db_restore=> :environment do
- backup_path_db = File.join(Gitlab.config.backup_path, "db")
-
- puts "Restoring database tables:"
- Rake::Task["db:reset"].invoke
-
- Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir|
- fixture_file = File.basename(dir, ".*" )
- print "- Loading fixture #{fixture_file}..."
- if File.size(dir) > 0
- ActiveRecord::Fixtures.create_fixtures(backup_path_db, fixture_file)
- puts "[DONE]".green
- else
- puts "[SKIPPING]".yellow
- end
- end
- end
-
- end # namespace end: app
+ end # namespace end: backup
end # namespace end: gitlab
diff --git a/lib/tasks/gitlab/bulk_add_permission.rake b/lib/tasks/gitlab/bulk_add_permission.rake
new file mode 100644
index 00000000..36c51d06
--- /dev/null
+++ b/lib/tasks/gitlab/bulk_add_permission.rake
@@ -0,0 +1,24 @@
+namespace :gitlab do
+ namespace :import do
+ desc "GITLAB | Add all users to all projects (admin users are added as masters)"
+ task :all_users_to_all_projects => :environment do |t, args|
+ user_ids = User.where(:admin => false).pluck(:id)
+ admin_ids = User.where(:admin => true).pluck(:id)
+
+ Project.find_each do |project|
+ puts "Importing #{user_ids.size} users into #{project.code}"
+ UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER)
+ puts "Importing #{admin_ids.size} admins into #{project.code}"
+ UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER)
+ end
+ end
+
+ desc "GITLAB | Add a specific user to all projects (as a developer)"
+ task :user_to_projects, [:email] => :environment do |t, args|
+ user = User.find_by_email args.email
+ project_ids = Project.pluck(:id)
+
+ UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER)
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
new file mode 100644
index 00000000..baa706d2
--- /dev/null
+++ b/lib/tasks/gitlab/check.rake
@@ -0,0 +1,968 @@
+namespace :gitlab do
+ desc "GITLAB | Check the configuration of GitLab and its environment"
+ task check: %w{gitlab:env:check
+ gitlab:gitolite:check
+ gitlab:resque:check
+ gitlab:app:check}
+
+
+
+ namespace :app do
+ desc "GITLAB | Check the configuration of the GitLab Rails app"
+ task check: :environment do
+ warn_user_is_not_gitlab
+ start_checking "GitLab"
+
+ check_database_config_exists
+ check_database_is_not_sqlite
+ check_migrations_are_up
+ check_gitlab_config_exists
+ check_gitlab_config_not_outdated
+ check_log_writable
+ check_tmp_writable
+ check_init_script_exists
+ check_init_script_up_to_date
+ check_satellites_exist
+
+ finished_checking "GitLab"
+ end
+
+
+ # Checks
+ ########################
+
+ def check_database_config_exists
+ print "Database config exists? ... "
+
+ database_config_file = Rails.root.join("config", "database.yml")
+
+ if File.exists?(database_config_file)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Copy config/database.yml. to config/database.yml",
+ "Check that the information in config/database.yml is correct"
+ )
+ for_more_information(
+ see_database_guide,
+ "http://guides.rubyonrails.org/getting_started.html#configuring-a-database"
+ )
+ check_failed
+ end
+ end
+
+ def check_database_is_not_sqlite
+ print "Database is not SQLite ... "
+
+ database_config_file = Rails.root.join("config", "database.yml")
+
+ unless File.read(database_config_file) =~ /sqlite/
+ puts "yes".green
+ else
+ puts "no".red
+ for_more_information(
+ "https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL",
+ see_database_guide
+ )
+ check_failed
+ end
+ end
+
+ def check_gitlab_config_exists
+ print "GitLab config exists? ... "
+
+ gitlab_config_file = Rails.root.join("config", "gitlab.yml")
+
+ if File.exists?(gitlab_config_file)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Copy config/gitlab.yml.example to config/gitlab.yml",
+ "Update config/gitlab.yml to match your setup"
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab"
+ )
+ check_failed
+ end
+ end
+
+ def check_gitlab_config_not_outdated
+ print "GitLab config outdated? ... "
+
+ gitlab_config_file = Rails.root.join("config", "gitlab.yml")
+ unless File.exists?(gitlab_config_file)
+ puts "can't check because of previous errors".magenta
+ end
+
+ # omniauth or ldap could have been deleted from the file
+ unless Gitlab.config.pre_40_config
+ puts "no".green
+ else
+ puts "yes".red
+ try_fixing_it(
+ "Backup your config/gitlab.yml",
+ "Copy config/gitlab.yml.example to config/gitlab.yml",
+ "Update config/gitlab.yml to match your setup"
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab"
+ )
+ check_failed
+ end
+ end
+
+ def check_init_script_exists
+ print "Init script exists? ... "
+
+ script_path = "/etc/init.d/gitlab"
+
+ if File.exists?(script_path)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Install the init script"
+ )
+ for_more_information(
+ see_installation_guide_section "Install Init Script"
+ )
+ check_failed
+ end
+ end
+
+ def check_init_script_up_to_date
+ print "Init script up-to-date? ... "
+
+ script_path = "/etc/init.d/gitlab"
+ unless File.exists?(script_path)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ recipe_content = `curl https://raw.github.com/gitlabhq/gitlab-recipes/master/init.d/gitlab 2>/dev/null`
+ script_content = File.read(script_path)
+
+ if recipe_content == script_content
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Redownload the init script"
+ )
+ for_more_information(
+ see_installation_guide_section "Install Init Script"
+ )
+ check_failed
+ end
+ end
+
+ def check_migrations_are_up
+ print "All migrations up? ... "
+
+ migration_status = `bundle exec rake db:migrate:status`
+
+ unless migration_status =~ /down\s+\d{14}/
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo -u gitlab -H bundle exec rake db:migrate"
+ )
+ check_failed
+ end
+ end
+
+ def check_satellites_exist
+ print "Projects have satellites? ... "
+
+ unless Project.count > 0
+ puts "can't check, you have no projects".magenta
+ return
+ end
+ puts ""
+
+ Project.find_each(batch_size: 100) do |project|
+ print "#{project.name_with_namespace.yellow} ... "
+
+ if project.satellite.exists?
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo -u gitlab -H bundle exec rake gitlab:satellites:create",
+ "If necessary, remove the tmp/repo_satellites directory ...",
+ "... and rerun the above command"
+ )
+ for_more_information(
+ "doc/raketasks/maintenance.md "
+ )
+ check_failed
+ end
+ end
+ end
+
+ def check_log_writable
+ print "Log directory writable? ... "
+
+ log_path = Rails.root.join("log")
+
+ if File.writable?(log_path)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo chown -R gitlab #{log_path}",
+ "sudo chmod -R rwX #{log_path}"
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab"
+ )
+ check_failed
+ end
+ end
+
+ def check_tmp_writable
+ print "Tmp directory writable? ... "
+
+ tmp_path = Rails.root.join("tmp")
+
+ if File.writable?(tmp_path)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo chown -R gitlab #{tmp_path}",
+ "sudo chmod -R rwX #{tmp_path}"
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab"
+ )
+ check_failed
+ end
+ end
+ end
+
+
+
+ namespace :env do
+ desc "GITLAB | Check the configuration of the environment"
+ task check: :environment do
+ warn_user_is_not_gitlab
+ start_checking "Environment"
+
+ check_gitlab_in_git_group
+ check_issue_1056_shell_profile_error
+ check_gitlab_git_config
+ check_python2_exists
+ check_python2_version
+
+ finished_checking "Environment"
+ end
+
+
+ # Checks
+ ########################
+
+ def check_gitlab_git_config
+ print "Git configured for gitlab user? ... "
+
+ options = {
+ "user.name" => "GitLab",
+ "user.email" => Gitlab.config.gitlab.email_from
+ }
+ correct_options = options.map do |name, value|
+ run("git config --global --get #{name}").try(:squish) == value
+ end
+
+ if correct_options.all?
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo -u gitlab -H git config --global user.name \"#{options["user.name"]}\"",
+ "sudo -u gitlab -H git config --global user.email \"#{options["user.email"]}\""
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab"
+ )
+ check_failed
+ end
+ end
+
+ def check_gitlab_in_git_group
+ gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
+ print "gitlab user is in #{gitolite_ssh_user} group? ... "
+
+ if run_and_match("id -rnG", /\Wgit\W/)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo usermod -a -G #{gitolite_ssh_user} gitlab"
+ )
+ for_more_information(
+ see_installation_guide_section "System Users"
+ )
+ check_failed
+ end
+ end
+
+ # see https://github.com/gitlabhq/gitlabhq/issues/1059
+ def check_issue_1056_shell_profile_error
+ gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
+ print "Has no \"-e\" in ~#{gitolite_ssh_user}/.profile ... "
+
+ profile_file = File.expand_path("~#{Gitlab.config.gitolite.ssh_user}/.profile")
+
+ unless File.read(profile_file) =~ /^-e PATH/
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Open #{profile_file}",
+ "Find the line starting with \"-e PATH\"",
+ "Remove \"-e \" so the line starts with PATH"
+ )
+ for_more_information(
+ see_installation_guide_section("Gitolite"),
+ "https://github.com/gitlabhq/gitlabhq/issues/1059"
+ )
+ check_failed
+ end
+ end
+
+ def check_python2_exists
+ print "Has python2? ... "
+
+ # Python prints its version to STDERR
+ # so we can't just use run("python2 --version")
+ if run_and_match("which python2", /python2$/)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Make sure you have Python 2.5+ installed",
+ "Link it to python2"
+ )
+ for_more_information(
+ see_installation_guide_section "Packages / Dependencies"
+ )
+ check_failed
+ end
+ end
+
+ def check_python2_version
+ print "python2 is supported version? ... "
+
+ # Python prints its version to STDERR
+ # so we can't just use run("python2 --version")
+
+ unless run_and_match("which python2", /python2$/)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ if `python2 --version 2>&1` =~ /2\.[567]\.\d/
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Make sure you have Python 2.5+ installed",
+ "Link it to python2"
+ )
+ for_more_information(
+ see_installation_guide_section "Packages / Dependencies"
+ )
+ check_failed
+ end
+ end
+ end
+
+
+
+ namespace :gitolite do
+ desc "GITLAB | Check the configuration of Gitolite"
+ task check: :environment do
+ warn_user_is_not_gitlab
+ start_checking "Gitolite"
+
+ check_gitolite_is_up_to_date
+ check_gitoliterc_repo_umask
+ check_gitoliterc_git_config_keys
+ check_dot_gitolite_exists
+ check_dot_gitolite_user_and_group
+ check_dot_gitolite_permissions
+ check_repo_base_exists
+ check_repo_base_user_and_group
+ check_repo_base_permissions
+ check_can_clone_gitolite_admin
+ check_can_commit_to_gitolite_admin
+ check_post_receive_hook_exists
+ check_post_receive_hook_is_up_to_date
+ check_repos_post_receive_hooks_is_link
+ check_repos_git_config
+
+ finished_checking "Gitolite"
+ end
+
+
+ # Checks
+ ########################
+
+ def check_can_clone_gitolite_admin
+ print "Can clone gitolite-admin? ... "
+
+ test_path = "/tmp/gitlab_gitolite_admin_test"
+ FileUtils.rm_rf(test_path)
+ `git clone -q #{Gitlab.config.gitolite.admin_uri} #{test_path}`
+ raise unless $?.success?
+
+ puts "yes".green
+ rescue
+ puts "no".red
+ try_fixing_it(
+ "Make sure the \"admin_uri\" is set correctly in config/gitlab.yml",
+ "Try cloning it yourself with:",
+ " git clone -q #{Gitlab.config.gitolite.admin_uri} /tmp/gitolite-admin",
+ "Make sure Gitolite is installed correctly."
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+
+ # assumes #check_can_clone_gitolite_admin has been run before
+ def check_can_commit_to_gitolite_admin
+ print "Can commit to gitolite-admin? ... "
+
+ test_path = "/tmp/gitlab_gitolite_admin_test"
+ unless File.exists?(test_path)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ Dir.chdir(test_path) do
+ `touch foo && git add foo && git commit -qm foo`
+ raise unless $?.success?
+ end
+
+ puts "yes".green
+ rescue
+ puts "no".red
+ try_fixing_it(
+ "Try committing to it yourself with:",
+ " git clone -q #{Gitlab.config.gitolite.admin_uri} /tmp/gitolite-admin",
+ " touch foo",
+ " git add foo",
+ " git commit -m \"foo\"",
+ "Make sure Gitolite is installed correctly."
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ ensure
+ FileUtils.rm_rf("/tmp/gitolite_gitlab_test")
+ end
+
+ def check_dot_gitolite_exists
+ print "Config directory exists? ... "
+
+ gitolite_config_path = File.expand_path("~#{Gitlab.config.gitolite.ssh_user}/.gitolite")
+
+ if File.directory?(gitolite_config_path)
+ puts "yes".green
+ else
+ puts "no".red
+ puts "#{gitolite_config_path} is missing".red
+ try_fixing_it(
+ "This should have been created when setting up Gitolite.",
+ "Make sure Gitolite is installed correctly."
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_dot_gitolite_permissions
+ print "Config directory access is drwxr-x---? ... "
+
+ gitolite_config_path = File.expand_path("~#{Gitlab.config.gitolite.ssh_user}/.gitolite")
+ unless File.exists?(gitolite_config_path)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ if `stat --printf %a #{gitolite_config_path}` == "750"
+ puts "yes".green
+ else
+ puts "no".red
+ puts "#{gitolite_config_path} is not writable".red
+ try_fixing_it(
+ "sudo chmod 750 #{gitolite_config_path}"
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_dot_gitolite_user_and_group
+ gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
+ print "Config directory owned by #{gitolite_ssh_user}:#{gitolite_ssh_user} ... "
+
+ gitolite_config_path = File.expand_path("~#{gitolite_ssh_user}/.gitolite")
+ unless File.exists?(gitolite_config_path)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ if `stat --printf %U #{gitolite_config_path}` == gitolite_ssh_user && # user
+ `stat --printf %G #{gitolite_config_path}` == gitolite_ssh_user #group
+ puts "yes".green
+ else
+ puts "no".red
+ puts "#{gitolite_config_path} is not owned by #{gitolite_ssh_user}".red
+ try_fixing_it(
+ "sudo chown -R #{gitolite_ssh_user}:#{gitolite_ssh_user} #{gitolite_config_path}"
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_gitolite_is_up_to_date
+ print "Using recommended version ... "
+ if gitolite_version.try(:start_with?, "v3.04")
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "We strongly recommend using the version pointed out in the installation guide."
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ # this is not a "hard" failure
+ end
+ end
+
+ def check_gitoliterc_git_config_keys
+ gitoliterc_path = File.join(gitolite_home, ".gitolite.rc")
+
+ print "Allow all Git config keys in .gitolite.rc ... "
+ option_name = if has_gitolite3?
+ # see https://github.com/sitaramc/gitolite/blob/v3.04/src/lib/Gitolite/Rc.pm#L329
+ "GIT_CONFIG_KEYS"
+ else
+ # assume older version
+ # see https://github.com/sitaramc/gitolite/blob/v2.3/conf/example.gitolite.rc#L49
+ "$GL_GITCONFIG_KEYS"
+ end
+ option_value = ".*"
+ if open(gitoliterc_path).grep(/#{option_name}\s*=[>]?\s*["']#{option_value}["']/).any?
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Open #{gitoliterc_path}",
+ "Find the \"#{option_name}\" option",
+ "Change its value to \".*\""
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_gitoliterc_repo_umask
+ gitoliterc_path = File.join(gitolite_home, ".gitolite.rc")
+
+ print "Repo umask is 0007 in .gitolite.rc? ... "
+ option_name = if has_gitolite3?
+ # see https://github.com/sitaramc/gitolite/blob/v3.04/src/lib/Gitolite/Rc.pm#L328
+ "UMASK"
+ else
+ # assume older version
+ # see https://github.com/sitaramc/gitolite/blob/v2.3/conf/example.gitolite.rc#L32
+ "$REPO_UMASK"
+ end
+ option_value = "0007"
+ if open(gitoliterc_path).grep(/#{option_name}\s*=[>]?\s*#{option_value}/).any?
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "Open #{gitoliterc_path}",
+ "Find the \"#{option_name}\" option",
+ "Change its value to \"0007\""
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_post_receive_hook_exists
+ print "post-receive hook exists? ... "
+
+ hook_file = "post-receive"
+ gitolite_hooks_path = File.join(Gitlab.config.gitolite.hooks_path, "common")
+ gitolite_hook_file = File.join(gitolite_hooks_path, hook_file)
+ gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
+
+ gitlab_hook_file = Rails.root.join.join("lib", "hooks", hook_file)
+
+ if File.exists?(gitolite_hook_file)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo -u #{gitolite_ssh_user} cp #{gitlab_hook_file} #{gitolite_hook_file}"
+ )
+ for_more_information(
+ see_installation_guide_section "Setup GitLab Hooks"
+ )
+ check_failed
+ end
+ end
+
+ def check_post_receive_hook_is_up_to_date
+ print "post-receive hook up-to-date? ... "
+
+ hook_file = "post-receive"
+ gitolite_hooks_path = File.join(Gitlab.config.gitolite.hooks_path, "common")
+ gitolite_hook_file = File.join(gitolite_hooks_path, hook_file)
+ gitolite_hook_content = File.read(gitolite_hook_file)
+ gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
+
+ unless File.exists?(gitolite_hook_file)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ gitlab_hook_file = Rails.root.join.join("lib", "hooks", hook_file)
+ gitlab_hook_content = File.read(gitlab_hook_file)
+
+ if gitolite_hook_content == gitlab_hook_content
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo -u #{gitolite_ssh_user} cp #{gitlab_hook_file} #{gitolite_hook_file}"
+ )
+ for_more_information(
+ see_installation_guide_section "Setup GitLab Hooks"
+ )
+ check_failed
+ end
+ end
+
+ def check_repo_base_exists
+ print "Repo base directory exists? ... "
+
+ repo_base_path = Gitlab.config.gitolite.repos_path
+
+ if File.exists?(repo_base_path)
+ puts "yes".green
+ else
+ puts "no".red
+ puts "#{repo_base_path} is missing".red
+ try_fixing_it(
+ "This should have been created when setting up Gitolite.",
+ "Make sure it's set correctly in config/gitlab.yml",
+ "Make sure Gitolite is installed correctly."
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_repo_base_permissions
+ print "Repo base access is drwsrws---? ... "
+
+ repo_base_path = Gitlab.config.gitolite.repos_path
+ unless File.exists?(repo_base_path)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ if `stat --printf %a #{repo_base_path}` == "6770"
+ puts "yes".green
+ else
+ puts "no".red
+ puts "#{repo_base_path} is not writable".red
+ try_fixing_it(
+ "sudo chmod -R ug+rwXs,o-rwx #{repo_base_path}"
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_repo_base_user_and_group
+ gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
+ print "Repo base owned by #{gitolite_ssh_user}:#{gitolite_ssh_user}? ... "
+
+ repo_base_path = Gitlab.config.gitolite.repos_path
+ unless File.exists?(repo_base_path)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ if `stat --printf %U #{repo_base_path}` == gitolite_ssh_user && # user
+ `stat --printf %G #{repo_base_path}` == gitolite_ssh_user #group
+ puts "yes".green
+ else
+ puts "no".red
+ puts "#{repo_base_path} is not owned by #{gitolite_ssh_user}".red
+ try_fixing_it(
+ "sudo chown -R #{gitolite_ssh_user}:#{gitolite_ssh_user} #{repo_base_path}"
+ )
+ for_more_information(
+ see_installation_guide_section "Gitolite"
+ )
+ check_failed
+ end
+ end
+
+ def check_repos_git_config
+ print "Git config in repos: ... "
+
+ unless Project.count > 0
+ puts "can't check, you have no projects".magenta
+ return
+ end
+ puts ""
+
+ options = {
+ "core.sharedRepository" => "0660",
+ }
+
+ Project.find_each(batch_size: 100) do |project|
+ print "#{project.name_with_namespace.yellow} ... "
+
+ correct_options = options.map do |name, value|
+ run("git --git-dir=\"#{project.path_to_repo}\" config --get #{name}").try(:chomp) == value
+ end
+
+ if correct_options.all?
+ puts "ok".green
+ else
+ puts "wrong or missing".red
+ try_fixing_it(
+ "sudo -u gitlab -H bundle exec rake gitlab:gitolite:update_repos"
+ )
+ for_more_information(
+ "doc/raketasks/maintenance.md"
+ )
+ check_failed
+ end
+ end
+ end
+
+ def check_repos_post_receive_hooks_is_link
+ print "post-receive hooks in repos are links: ... "
+
+ hook_file = "post-receive"
+ gitolite_hooks_path = File.join(Gitlab.config.gitolite.hooks_path, "common")
+ gitolite_hook_file = File.join(gitolite_hooks_path, hook_file)
+ gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
+
+ unless File.exists?(gitolite_hook_file)
+ puts "can't check because of previous errors".magenta
+ return
+ end
+
+ unless Project.count > 0
+ puts "can't check, you have no projects".magenta
+ return
+ end
+ puts ""
+
+ Project.find_each(batch_size: 100) do |project|
+ print "#{project.name_with_namespace.yellow} ... "
+ project_hook_file = File.join(project.path_to_repo, "hooks", hook_file)
+
+ unless File.exists?(project_hook_file)
+ puts "missing".red
+ try_fixing_it(
+ "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}"
+ )
+ for_more_information(
+ "lib/support/rewrite-hooks.sh"
+ )
+ check_failed
+ next
+ end
+
+ if run_and_match("stat --format %N #{project_hook_file}", /#{hook_file}.+->.+#{gitolite_hook_file}/)
+ puts "ok".green
+ else
+ puts "not a link to Gitolite's hook".red
+ try_fixing_it(
+ "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}"
+ )
+ for_more_information(
+ "lib/support/rewrite-hooks.sh"
+ )
+ check_failed
+ end
+ end
+ end
+
+
+ # Helper methods
+ ########################
+
+ def gitolite_home
+ File.expand_path("~#{Gitlab.config.gitolite.ssh_user}")
+ end
+
+ def gitolite_version
+ gitolite_version_file = "#{gitolite_home}/gitolite/src/VERSION"
+ if File.readable?(gitolite_version_file)
+ File.read(gitolite_version_file)
+ end
+ end
+
+ def has_gitolite3?
+ gitolite_version.try(:start_with?, "v3.")
+ end
+ end
+
+
+
+ namespace :resque do
+ desc "GITLAB | Check the configuration of Resque"
+ task check: :environment do
+ warn_user_is_not_gitlab
+ start_checking "Resque"
+
+ check_resque_running
+
+ finished_checking "Resque"
+ end
+
+
+ # Checks
+ ########################
+
+ def check_resque_running
+ print "Running? ... "
+
+ if run_and_match("ps aux | grep -i resque", /resque-[\d\.]+:.+$/)
+ puts "yes".green
+ else
+ puts "no".red
+ try_fixing_it(
+ "sudo service gitlab restart",
+ "or",
+ "sudo /etc/init.d/gitlab restart"
+ )
+ for_more_information(
+ see_installation_guide_section("Install Init Script"),
+ "see log/resque.log for possible errors"
+ )
+ check_failed
+ end
+ end
+ end
+
+
+ # Helper methods
+ ##########################
+
+ def check_failed
+ puts " Please #{"fix the error above"} and rerun the checks.".red
+ end
+
+ def for_more_information(*sources)
+ sources = sources.shift if sources.first.is_a?(Array)
+
+ puts " For more information see:".blue
+ sources.each do |source|
+ puts " #{source}"
+ end
+ end
+
+ def finished_checking(component)
+ puts ""
+ puts "Checking #{component.yellow} ... #{"Finished".green}"
+ puts ""
+ end
+
+ # Runs the given command
+ #
+ # Returns nil if the command was not found
+ # Returns the output of the command otherwise
+ #
+ # see also #run_and_match
+ def run(command)
+ unless `#{command} 2>/dev/null`.blank?
+ `#{command}`
+ end
+ end
+
+ # Runs the given command and matches the output agains the given pattern
+ #
+ # Returns nil if nothing matched
+ # Retunrs the MatchData if the pattern matched
+ #
+ # see also #run
+ # see also String#match
+ def run_and_match(command, pattern)
+ run(command).try(:match, pattern)
+ end
+
+ def see_database_guide
+ "doc/install/databases.md"
+ end
+
+ def see_installation_guide_section(section)
+ "doc/install/installation.md in section \"#{section}\""
+ end
+
+ def start_checking(component)
+ puts "Checking #{component.yellow} ..."
+ puts ""
+ end
+
+ def try_fixing_it(*steps)
+ steps = steps.shift if steps.first.is_a?(Array)
+
+ puts " Try fixing it:".blue
+ steps.each do |step|
+ puts " #{step}"
+ end
+ end
+
+ def warn_user_is_not_gitlab
+ unless @warned_user_not_gitlab
+ current_user = run("whoami").chomp
+ unless current_user == "gitlab"
+ puts "#{Colored.color(:black)+Colored.color(:on_yellow)} Warning #{Colored.extra(:clear)}"
+ puts " You are running as user #{current_user.magenta}, we hope you know what you are doing."
+ puts " Some tests may pass\/fail for the wrong reason."
+ puts " For meaningful results you should run this as user #{"gitlab".magenta}."
+ puts ""
+ end
+ @warned_user_not_gitlab = true
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/enable_automerge.rake b/lib/tasks/gitlab/enable_automerge.rake
index 13b4bab6..ed3d6368 100644
--- a/lib/tasks/gitlab/enable_automerge.rake
+++ b/lib/tasks/gitlab/enable_automerge.rake
@@ -1,17 +1,20 @@
namespace :gitlab do
- namespace :app do
- desc "GITLAB | Enable auto merge"
- task :enable_automerge => :environment do
- Gitlab::Gitolite.new.enable_automerge
+ desc "GITLAB | Enable auto merge"
+ task :enable_automerge => :environment do
+ Gitlab::Gitolite.new.enable_automerge
- Project.find_each do |project|
- if project.repo_exists? && !project.satellite.exists?
- puts "Creating satellite for #{project.name}...".green
- project.satellite.create
- end
+ Project.find_each do |project|
+ if project.repo_exists? && !project.satellite.exists?
+ puts "Creating satellite for #{project.name}...".green
+ project.satellite.create
end
-
- puts "Done!".green
end
+
+ puts "Done!".green
+ end
+
+ namespace :satellites do
+ desc "GITLAB | Create satellite repos"
+ task create: 'gitlab:enable_automerge'
end
end
diff --git a/lib/tasks/gitlab/activate_namespaces.rake b/lib/tasks/gitlab/enable_namespaces.rake
similarity index 94%
rename from lib/tasks/gitlab/activate_namespaces.rake
rename to lib/tasks/gitlab/enable_namespaces.rake
index 08df0a80..1be9ba64 100644
--- a/lib/tasks/gitlab/activate_namespaces.rake
+++ b/lib/tasks/gitlab/enable_namespaces.rake
@@ -1,6 +1,6 @@
namespace :gitlab do
desc "GITLAB | Enable usernames and namespaces for user projects"
- task activate_namespaces: :environment do
+ task enable_namespaces: :environment do
print "\nUsernames for users:".yellow
User.find_each(batch_size: 500) do |user|
@@ -27,7 +27,7 @@ namespace :gitlab do
end
print "\n\nMove projects from groups under groups dirs:".yellow
- git_path = Gitlab.config.git_base_path
+ git_path = Gitlab.config.gitolite.repos_path
Project.where('namespace_id IS NOT NULL').find_each(batch_size: 500) do |project|
next unless project.group
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index 09f0dc9e..81f66e2e 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -12,7 +12,7 @@ namespace :gitlab do
desc "GITLAB | Import bare repositories from git_host -> base_path into GitLab project instance"
task :repos => :environment do
- git_base_path = Gitlab.config.git_base_path
+ git_base_path = Gitlab.config.gitolite.repos_path
repos_to_import = Dir.glob(git_base_path + '/*')
repos_to_import.each do |repo_path|
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
new file mode 100644
index 00000000..85458fe2
--- /dev/null
+++ b/lib/tasks/gitlab/info.rake
@@ -0,0 +1,110 @@
+namespace :gitlab do
+ namespace :env do
+ desc "GITLAB | Show information about GitLab and its environment"
+ task info: :environment do
+
+ # check which OS is running
+ os_name = run("lsb_release -irs")
+ os_name ||= if File.readable?('/etc/system-release')
+ File.read('/etc/system-release')
+ end
+ os_name ||= if File.readable?('/etc/debian_version')
+ debian_version = File.read('/etc/debian_version')
+ "Debian #{debian_version}"
+ end
+ os_name.squish!
+
+ # check if there is an RVM environment
+ rvm_version = run_and_match("rvm --version", /[\d\.]+/).try(:to_s)
+ # check Ruby version
+ ruby_version = run_and_match("ruby --version", /[\d\.p]+/).try(:to_s)
+ # check Gem version
+ gem_version = run("gem --version")
+ # check Bundler version
+ bunder_version = run_and_match("bundle --version", /[\d\.]+/).try(:to_s)
+ # check Bundler version
+ rake_version = run_and_match("rake --version", /[\d\.]+/).try(:to_s)
+
+ puts ""
+ puts "System information".yellow
+ puts "System:\t\t#{os_name || "unknown".red}"
+ puts "Current User:\t#{`whoami`}"
+ puts "Using RVM:\t#{rvm_version.present? ? "yes".green : "no"}"
+ puts "RVM Version:\t#{rvm_version}" if rvm_version.present?
+ puts "Ruby Version:\t#{ruby_version || "unknown".red}"
+ puts "Gem Version:\t#{gem_version || "unknown".red}"
+ puts "Bundler Version:#{bunder_version || "unknown".red}"
+ puts "Rake Version:\t#{rake_version || "unknown".red}"
+
+
+ # check database adapter
+ database_adapter = ActiveRecord::Base.connection.adapter_name.downcase
+
+ project = Project.new(path: "some-project")
+ project.path = "some-project"
+ # construct clone URLs
+ http_clone_url = project.http_url_to_repo
+ ssh_clone_url = project.ssh_url_to_repo
+
+ omniauth_providers = Gitlab.config.omniauth.providers
+ omniauth_providers.map! { |provider| provider['name'] }
+
+ puts ""
+ puts "GitLab information".yellow
+ puts "Version:\t#{Gitlab::Version}"
+ puts "Revision:\t#{Gitlab::Revision}"
+ puts "Directory:\t#{Rails.root}"
+ puts "DB Adapter:\t#{database_adapter}"
+ puts "URL:\t\t#{Gitlab.config.gitlab.url}"
+ puts "HTTP Clone URL:\t#{http_clone_url}"
+ puts "SSH Clone URL:\t#{ssh_clone_url}"
+ puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".green : "no"}"
+ puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".green : "no"}"
+ puts "Omniauth Providers: #{omniauth_providers.map(&:magenta).join(', ')}" if Gitlab.config.omniauth.enabled
+
+
+
+ # check Gitolite version
+ gitolite_version_file = "#{Gitlab.config.gitolite.repos_path}/../gitolite/src/VERSION"
+ if File.exists?(gitolite_version_file) && File.readable?(gitolite_version_file)
+ gitolite_version = File.read(gitolite_version_file)
+ end
+
+ puts ""
+ puts "Gitolite information".yellow
+ puts "Version:\t#{gitolite_version || "unknown".red}"
+ puts "Admin URI:\t#{Gitlab.config.gitolite.admin_uri}"
+ puts "Admin Key:\t#{Gitlab.config.gitolite.admin_key}"
+ puts "Repositories:\t#{Gitlab.config.gitolite.repos_path}"
+ puts "Hooks:\t\t#{Gitlab.config.gitolite.hooks_path}"
+ puts "Git:\t\t#{Gitlab.config.git.bin_path}"
+
+ end
+
+
+ # Helper methods
+
+ # Runs the given command and matches the output agains the given pattern
+ #
+ # Returns nil if nothing matched
+ # Retunrs the MatchData if the pattern matched
+ #
+ # see also #run
+ # see also String#match
+ def run_and_match(command, regexp)
+ run(command).try(:match, regexp)
+ end
+
+ # Runs the given command
+ #
+ # Returns nil if the command was not found
+ # Returns the output of the command otherwise
+ #
+ # see also #run_and_match
+ def run(command)
+ unless `#{command} 2>/dev/null`.blank?
+ `#{command}`
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake
index 08f35c7e..572a22aa 100644
--- a/lib/tasks/gitlab/setup.rake
+++ b/lib/tasks/gitlab/setup.rake
@@ -4,7 +4,7 @@ namespace :gitlab do
task :setup => [
'db:setup',
'db:seed_fu',
- 'gitlab:app:enable_automerge'
+ 'gitlab:enable_automerge'
]
end
end
diff --git a/lib/tasks/gitlab/status.rake b/lib/tasks/gitlab/status.rake
deleted file mode 100644
index cbc77abb..00000000
--- a/lib/tasks/gitlab/status.rake
+++ /dev/null
@@ -1,113 +0,0 @@
-namespace :gitlab do
- namespace :app do
- desc "GITLAB | Check GitLab installation status"
- task :status => :environment do
- puts "\nStarting diagnostics".yellow
- git_base_path = Gitlab.config.git_base_path
-
- print "config/database.yml............"
- if File.exists?(Rails.root.join "config", "database.yml")
- puts "exists".green
- else
- puts "missing".red
- return
- end
-
- print "config/gitlab.yml............"
- if File.exists?(Rails.root.join "config", "gitlab.yml")
- puts "exists".green
- else
- puts "missing".red
- return
- end
-
- print "#{git_base_path}............"
- if File.exists?(git_base_path)
- puts "exists".green
- else
- puts "missing".red
- return
- end
-
- print "#{git_base_path} is writable?............"
- if File.stat(git_base_path).writable?
- puts "YES".green
- else
- puts "NO".red
- return
- end
-
- FileUtils.rm_rf("/tmp/gitolite_gitlab_test")
- begin
- `git clone -q #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test`
- raise unless $?.success?
- print "Can clone gitolite-admin?............"
- puts "YES".green
- rescue
- print "Can clone gitolite-admin?............"
- puts "NO".red
- return
- end
-
- begin
- Dir.chdir("/tmp/gitolite_gitlab_test") do
- `touch blah && git add blah && git commit -qm blah -- blah`
- raise unless $?.success?
- end
- print "Can git commit?............"
- puts "YES".green
- rescue
- print "Can git commit?............"
- puts "NO".red
- return
- ensure
- FileUtils.rm_rf("/tmp/gitolite_gitlab_test")
- end
-
- print "UMASK for .gitolite.rc is 0007? ............"
- if open(File.absolute_path("#{git_base_path}/../.gitolite.rc")).grep(/UMASK([ \t]*)=([ \t>]*)0007/).any?
- puts "YES".green
- else
- puts "NO".red
- return
- end
-
- gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common")
- gitlab_hook_files = ['post-receive']
- gitlab_hook_files.each do |file_name|
- dest = File.join(gitolite_hooks_path, file_name)
- print "#{dest} exists? ............"
- if File.exists?(dest)
- puts "YES".green
- else
- puts "NO".red
- return
- end
- end
-
- if Project.count > 0
- puts "\nValidating projects repositories:".yellow
- Project.find_each(:batch_size => 100) do |project|
- print "* #{project.name}....."
- hook_file = File.join(project.path_to_repo, 'hooks', 'post-receive')
-
- unless File.exists?(hook_file)
- puts "post-receive file missing".red
- next
- end
-
- original_content = File.read(Rails.root.join('lib', 'hooks', 'post-receive'))
- new_content = File.read(hook_file)
-
- if original_content == new_content
- puts "post-receive file ok".green
- else
- puts "post-receive file content does not match".red
- end
- end
- end
-
- puts "\nFinished".blue
- end
- end
-end
diff --git a/lib/tasks/resque.rake b/lib/tasks/resque.rake
index e6987e17..0825324a 100644
--- a/lib/tasks/resque.rake
+++ b/lib/tasks/resque.rake
@@ -1,12 +1,8 @@
require 'resque/tasks'
-# Fix Exception
-# ActiveRecord::StatementInvalid
-# Error
-# PGError: ERROR: prepared statement "a3" already exists
task "resque:setup" => :environment do
- Resque.after_fork do |job|
- ActiveRecord::Base.establish_connection
+ Resque.after_fork do
+ Resque.redis.client.reconnect
end
end
diff --git a/lib/tasks/travis.rake b/lib/tasks/travis.rake
index 13e32135..e04bfbaf 100644
--- a/lib/tasks/travis.rake
+++ b/lib/tasks/travis.rake
@@ -1,5 +1,5 @@
task :travis do
- ["spinach", "rspec spec"].each do |cmd|
+ ["rake spinach", "rake spec"].each do |cmd|
puts "Starting to run #{cmd}..."
system("export DISPLAY=:99.0 && bundle exec #{cmd}")
raise "#{cmd} failed!" unless $?.exitstatus == 0
diff --git a/spec/factories.rb b/spec/factories.rb
index ac49f14c..44fb9378 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -45,6 +45,7 @@ FactoryGirl.define do
factory :users_project do
user
project
+ project_access { UsersProject::MASTER }
end
factory :issue do
@@ -100,7 +101,7 @@ FactoryGirl.define do
factory :note_on_merge_request_line, traits: [:on_merge_request, :on_line]
trait :on_commit do
- noteable_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
+ commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
noteable_type "Commit"
end
@@ -114,7 +115,7 @@ FactoryGirl.define do
end
trait :on_issue do
- noteable_id 1
+ noteable_id 1
noteable_type "Issue"
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index a94d5505..ba1af084 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -43,7 +43,7 @@ describe ApplicationHelper do
let(:user_email) { 'user@email.com' }
it "should return a generic avatar path when Gravatar is disabled" do
- Gitlab.config.stub(:disable_gravatar?).and_return(true)
+ Gitlab.config.gravatar.stub(:enabled).and_return(false)
gravatar_icon(user_email).should == 'no_avatar.png'
end
@@ -51,14 +51,36 @@ describe ApplicationHelper do
gravatar_icon('').should == 'no_avatar.png'
end
+ it "should return default gravatar url" do
+ stub!(:request).and_return(double(:ssl? => false))
+ gravatar_icon(user_email).should match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+ end
+
it "should use SSL when appropriate" do
stub!(:request).and_return(double(:ssl? => true))
gravatar_icon(user_email).should match('https://secure.gravatar.com')
end
+ it "should return custom gravatar path when gravatar_url is set" do
+ stub!(:request).and_return(double(:ssl? => false))
+ Gitlab.config.gravatar.stub(:plain_url).and_return('http://example.local/?s=%{size}&hash=%{hash}')
+ gravatar_icon(user_email, 20).should == 'http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118'
+ end
+
it "should accept a custom size" do
stub!(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email, 64).should match(/\?s=64/)
end
+
+ it "should use default size when size is wrong" do
+ stub!(:request).and_return(double(:ssl? => false))
+ gravatar_icon(user_email, nil).should match(/\?s=40/)
+ end
+
+ it "should be case insensitive" do
+ stub!(:request).and_return(double(:ssl? => false))
+ gravatar_icon(user_email).should == gravatar_icon(user_email.upcase + " ")
+ end
+
end
end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 05e4527b..b792e0c8 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -3,7 +3,7 @@ require "spec_helper"
describe GitlabMarkdownHelper do
let!(:project) { create(:project) }
- let(:user) { create(:user, name: 'gfm') }
+ let(:user) { create(:user, username: 'gfm') }
let(:commit) { CommitDecorator.decorate(project.commit) }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, project: project) }
@@ -81,11 +81,11 @@ describe GitlabMarkdownHelper do
end
describe "referencing a team member" do
- let(:actual) { "@#{user.name} you are right." }
+ let(:actual) { "@#{user.username} you are right." }
let(:expected) { project_team_member_path(project, member) }
before do
- project.users << user
+ project.add_access(user, :admin)
end
it "should link using a simple name" do
@@ -103,18 +103,18 @@ describe GitlabMarkdownHelper do
end
it "should link with adjacent text" do
- actual = "Mail the admin (@gfm)"
+ actual = "Mail the admin (@#{user.username})"
gfm(actual).should match(expected)
end
it "should keep whitespace intact" do
- actual = "Yes, @#{user.name} is right."
- expected = /Yes, @#{user.name}<\/a> is right/
+ actual = "Yes, @#{user.username} is right."
+ expected = /Yes, @#{user.username}<\/a> is right/
gfm(actual).should match(expected)
end
it "should not link with an invalid id" do
- actual = expected = "@#{user.name.reverse} you are right."
+ actual = expected = "@#{user.username.reverse} you are right."
gfm(actual).should == expected
end
@@ -314,12 +314,12 @@ describe GitlabMarkdownHelper do
end
it "should handle references in lists" do
- project.users << user
+ project.add_access(user, :admin)
- actual = "\n* dark: ##{issue.id}\n* light by @#{member.user_name}"
+ actual = "\n* dark: ##{issue.id}\n* light by @#{member.user.username}"
markdown(actual).should match(%r{dark: ##{issue.id}})
- markdown(actual).should match(%r{light by @#{member.user_name}})
+ markdown(actual).should match(%r{light by @#{member.user.username}})
end
it "should handle references in " do
@@ -331,9 +331,9 @@ describe GitlabMarkdownHelper do
it "should leave code blocks untouched" do
helper.stub(:user_color_scheme_class).and_return(:white)
- helper.markdown("\n some code from $#{snippet.id}\n here too\n").should == "some code from $#{snippet.id}\nhere too\n
"
+ helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include("some code from $#{snippet.id}\nhere too\n
")
- helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should == "some code from $#{snippet.id}\nhere too\n
"
+ helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include("some code from $#{snippet.id}\nhere too\n
")
end
it "should leave inline code untouched" do
diff --git a/spec/lib/gitolite_spec.rb b/spec/lib/gitolite_spec.rb
index cc8ce8b2..8075b99e 100644
--- a/spec/lib/gitolite_spec.rb
+++ b/spec/lib/gitolite_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::Gitolite do
it { should respond_to :create_repository }
it { should respond_to :remove_repository }
- it { gitolite.url_to_repo('diaspora').should == Gitlab.config.ssh_path + "diaspora.git" }
+ it { gitolite.url_to_repo('diaspora').should == Gitlab.config.gitolite.ssh_path_prefix + "diaspora.git" }
it "should call config update" do
gitolite_config.should_receive(:update_project!)
diff --git a/spec/lib/project_mover_spec.rb b/spec/lib/project_mover_spec.rb
index af24635d..2362bc26 100644
--- a/spec/lib/project_mover_spec.rb
+++ b/spec/lib/project_mover_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::ProjectMover do
before do
FileUtils.rm_rf base_path if File.exists? base_path
- Gitlab.config.stub(git_base_path: base_path)
+ Gitlab.config.gitolite.stub(repos_path: base_path)
@project = create(:project)
end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 49cb49db..82b46b68 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -59,7 +59,7 @@ describe Event do
end
it { @event.push?.should be_true }
- it { @event.allowed?.should be_true }
+ it { @event.proper?.should be_true }
it { @event.new_branch?.should be_true }
it { @event.tag?.should be_false }
it { @event.branch_name.should == "master" }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index d70647f6..a0849401 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -42,7 +42,7 @@ describe MergeRequest do
before do
merge_request.stub(:commits) { [merge_request.project.commit] }
- create(:note, noteable: merge_request.commits.first)
+ create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit')
create(:note, noteable: merge_request)
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 4c1afd8a..34683e41 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -80,13 +80,13 @@ describe Note do
let!(:commit) { note.noteable }
it "should be accessible through #noteable" do
- note.noteable_id.should == commit.id
+ note.commit_id.should == commit.id
note.noteable.should be_a(Commit)
note.noteable.should == commit
end
it "should save a valid note" do
- note.noteable_id.should == commit.id
+ note.commit_id.should == commit.id
note.noteable == commit
end
@@ -104,7 +104,7 @@ describe Note do
let!(:commit) { note.noteable }
it "should save a valid note" do
- note.noteable_id.should == commit.id
+ note.commit_id.should == commit.id
note.noteable.id.should == commit.id
end
diff --git a/spec/models/project_hooks_spec.rb b/spec/models/project_hooks_spec.rb
index 7c8f05b1..df6a3831 100644
--- a/spec/models/project_hooks_spec.rb
+++ b/spec/models/project_hooks_spec.rb
@@ -108,7 +108,7 @@ describe Project, "Hooks" do
it { should include(id: @commit.id) }
it { should include(message: @commit.safe_message) }
it { should include(timestamp: @commit.date.xmlschema) }
- it { should include(url: "#{Gitlab.config.url}/#{project.code}/commits/#{@commit.id}") }
+ it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") }
context "with a author" do
subject { @data[:commits].first[:author] }
diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb
index 60f8d45c..92c6bce0 100644
--- a/spec/models/project_security_spec.rb
+++ b/spec/models/project_security_spec.rb
@@ -4,38 +4,109 @@ describe Project do
describe :authorization do
before do
@p1 = create(:project)
+
@u1 = create(:user)
@u2 = create(:user)
+ @u3 = create(:user)
+ @u4 = @p1.chief
+
@abilities = Six.new
@abilities << Ability
end
- describe "read access" do
+ let(:guest_actions) { Ability.project_guest_rules }
+ let(:report_actions) { Ability.project_report_rules }
+ let(:dev_actions) { Ability.project_dev_rules }
+ let(:master_actions) { Ability.project_master_rules }
+ let(:admin_actions) { Ability.project_admin_rules }
+
+ describe "Non member rules" do
+ it "should deny for non-project users any actions" do
+ admin_actions.each do |action|
+ @abilities.allowed?(@u1, action, @p1).should be_false
+ end
+ end
+ end
+
+ describe "Guest Rules" do
+ before do
+ @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::GUEST)
+ end
+
+ it "should allow for project user any guest actions" do
+ guest_actions.each do |action|
+ @abilities.allowed?(@u2, action, @p1).should be_true
+ end
+ end
+ end
+
+ describe "Report Rules" do
before do
@p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::REPORTER)
end
- it { @abilities.allowed?(@u1, :read_project, @p1).should be_false }
- it { @abilities.allowed?(@u2, :read_project, @p1).should be_true }
+ it "should allow for project user any report actions" do
+ report_actions.each do |action|
+ @abilities.allowed?(@u2, action, @p1).should be_true
+ end
+ end
end
- describe "write access" do
+ describe "Developer Rules" do
+ before do
+ @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::REPORTER)
+ @p1.users_projects.create(project: @p1, user: @u3, project_access: UsersProject::DEVELOPER)
+ end
+
+ it "should deny for developer master-specific actions" do
+ [dev_actions - report_actions].each do |action|
+ @abilities.allowed?(@u2, action, @p1).should be_false
+ end
+ end
+
+ it "should allow for project user any dev actions" do
+ dev_actions.each do |action|
+ @abilities.allowed?(@u3, action, @p1).should be_true
+ end
+ end
+ end
+
+ describe "Master Rules" do
before do
@p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::DEVELOPER)
+ @p1.users_projects.create(project: @p1, user: @u3, project_access: UsersProject::MASTER)
end
- it { @abilities.allowed?(@u1, :write_project, @p1).should be_false }
- it { @abilities.allowed?(@u2, :write_project, @p1).should be_true }
+ it "should deny for developer master-specific actions" do
+ [master_actions - dev_actions].each do |action|
+ @abilities.allowed?(@u2, action, @p1).should be_false
+ end
+ end
+
+ it "should allow for project user any master actions" do
+ master_actions.each do |action|
+ @abilities.allowed?(@u3, action, @p1).should be_true
+ end
+ end
end
- describe "admin access" do
+ describe "Admin Rules" do
before do
- @p1.users_projects.create(project: @p1, user: @u1, project_access: UsersProject::DEVELOPER)
- @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::MASTER)
+ @p1.users_projects.create(project: @p1, user: @u2, project_access: UsersProject::DEVELOPER)
+ @p1.users_projects.create(project: @p1, user: @u3, project_access: UsersProject::MASTER)
end
- it { @abilities.allowed?(@u1, :admin_project, @p1).should be_false }
- it { @abilities.allowed?(@u2, :admin_project, @p1).should be_true }
+ it "should deny for masters admin-specific actions" do
+ [admin_actions - master_actions].each do |action|
+ @abilities.allowed?(@u2, action, @p1).should be_false
+ end
+ end
+
+ it "should allow for project owner any admin actions" do
+ admin_actions.each do |action|
+ @abilities.allowed?(@u4, action, @p1).should be_true
+ end
+ end
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index db0d3072..83a76976 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -129,6 +129,13 @@ describe Project do
it { should respond_to(:execute_hooks) }
it { should respond_to(:post_receive_data) }
it { should respond_to(:trigger_post_receive) }
+
+ # Namespaced Project Role
+ it { should respond_to(:transfer) }
+ it { should respond_to(:name_with_namespace) }
+ it { should respond_to(:namespace_owner) }
+ it { should respond_to(:chief) }
+ it { should respond_to(:path_with_namespace) }
end
describe 'modules' do
@@ -136,11 +143,12 @@ describe Project do
it { should include_module(PushObserver) }
it { should include_module(Authority) }
it { should include_module(Team) }
+ it { should include_module(NamespacedProject) }
end
it "should return valid url to repo" do
project = Project.new(path: "somewhere")
- project.url_to_repo.should == Gitlab.config.ssh_path + "somewhere.git"
+ project.url_to_repo.should == Gitlab.config.gitolite.ssh_path_prefix + "somewhere.git"
end
it "should return path to repo" do
@@ -150,19 +158,7 @@ describe Project do
it "returns the full web URL for this repo" do
project = Project.new(path: "somewhere")
- project.web_url.should == "#{Gitlab.config.url}/somewhere"
- end
-
- describe :valid_repo? do
- it "should be valid repo" do
- project = create(:project)
- project.valid_repo?.should be_true
- end
-
- it "should be invalid repo" do
- project = Project.new(name: "ok_name", path: "/INVALID_PATH/", path: "NEOK")
- project.valid_repo?.should be_false
- end
+ project.web_url.should == "#{Gitlab.config.gitlab.url}/somewhere"
end
describe "last_activity methods" do
@@ -188,85 +184,6 @@ describe Project do
end
end
- describe "fresh commits" do
- let(:project) { create(:project) }
-
- it { project.fresh_commits(3).count.should == 3 }
- it { project.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" }
- it { project.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" }
- end
-
- describe "commits_between" do
- let(:project) { create(:project) }
-
- subject do
- commits = project.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff",
- "8470d70da67355c9c009e4401746b1d5410af2e3")
- commits.map { |c| c.id }
- end
-
- it { should have(3).elements }
- it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") }
- it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }
- end
-
- describe "Git methods" do
- let(:project) { create(:project) }
-
- describe :repo do
- it "should return valid repo" do
- project.repo.should be_kind_of(Grit::Repo)
- end
-
- it "should return nil" do
- lambda { Project.new(path: "invalid").repo }.should raise_error(Grit::NoSuchPathError)
- end
-
- it "should return nil" do
- lambda { Project.new.repo }.should raise_error(TypeError)
- end
- end
-
- describe :commit do
- it "should return first head commit if without params" do
- project.commit.id.should == project.repo.commits.first.id
- end
-
- it "should return valid commit" do
- project.commit(ValidCommit::ID).should be_valid_commit
- end
-
- it "should return nil" do
- project.commit("+123_4532530XYZ").should be_nil
- end
- end
-
- describe :tree do
- before do
- @commit = project.commit(ValidCommit::ID)
- end
-
- it "should raise error w/o arguments" do
- lambda { project.tree }.should raise_error
- end
-
- it "should return root tree for commit" do
- tree = project.tree(@commit)
- tree.contents.size.should == ValidCommit::FILES_COUNT
- tree.contents.map(&:name).should == ValidCommit::FILES
- end
-
- it "should return root tree for commit with correct path" do
- tree = project.tree(@commit, ValidCommit::C_FILE_PATH)
- tree.contents.map(&:name).should == ValidCommit::C_FILES
- end
-
- it "should return root tree for commit with incorrect path" do
- project.tree(@commit, "invalid_path").should be_nil
- end
- end
- end
-
describe :update_merge_requests do
let(:project) { create(:project) }
diff --git a/spec/models/system_hook_spec.rb b/spec/models/system_hook_spec.rb
index 9d03b56c..7ae483a4 100644
--- a/spec/models/system_hook_spec.rb
+++ b/spec/models/system_hook_spec.rb
@@ -56,7 +56,7 @@ describe SystemHook do
user = create(:user)
project = create(:project)
with_resque do
- project.users << user
+ project.add_access(user, :admin)
end
WebMock.should have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once
end
@@ -64,7 +64,7 @@ describe SystemHook do
it "project_destroy hook" do
user = create(:user)
project = create(:project)
- project.users << user
+ project.add_access(user, :admin)
with_resque do
project.users_projects.clear
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 279e315b..d09484f8 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -41,7 +41,6 @@ describe User do
it { should have_many(:users_projects).dependent(:destroy) }
it { should have_many(:projects) }
it { should have_many(:groups) }
- it { should have_many(:my_own_projects).class_name('Project') }
it { should have_many(:keys).dependent(:destroy) }
it { should have_many(:events).class_name('Event').dependent(:destroy) }
it { should have_many(:recent_events).class_name('Event') }
@@ -67,6 +66,10 @@ describe User do
it { should ensure_length_of(:bio).is_within(0..255) }
end
+ describe 'modules' do
+ it { should include_module(Account) }
+ end
+
describe "Respond to" do
it { should respond_to(:is_admin?) }
it { should respond_to(:identifier) }
diff --git a/spec/models/users_project_spec.rb b/spec/models/users_project_spec.rb
index 1f896324..a9a1857e 100644
--- a/spec/models/users_project_spec.rb
+++ b/spec/models/users_project_spec.rb
@@ -29,6 +29,7 @@ describe UsersProject do
it { should validate_uniqueness_of(:user_id).scoped_to(:project_id).with_message(/already exists/) }
it { should validate_presence_of(:project) }
+ it { should ensure_inclusion_of(:project_access).in_array(UsersProject.access_roles.values) }
end
describe "Delegate methods" do
diff --git a/spec/observers/activity_observer_spec.rb b/spec/observers/activity_observer_spec.rb
index 0eec41f4..6af5d070 100644
--- a/spec/observers/activity_observer_spec.rb
+++ b/spec/observers/activity_observer_spec.rb
@@ -34,15 +34,17 @@ describe ActivityObserver do
it { @event.target.should == @issue }
end
- #describe "Issue commented" do
- #before do
- #@issue = create(:issue, project: project)
- #@note = create(:note, noteable: @issue, project: project)
- #@event = Event.last
- #end
+ describe "Issue commented" do
+ before do
+ Note.observers.enable :activity_observer do
+ @issue = create(:issue, project: project)
+ @note = create(:note, noteable: @issue, project: project, author: @issue.author)
+ @event = Event.last
+ end
+ end
- #it_should_be_valid_event
- #it { @event.action.should == Event::Commented }
- #it { @event.target.should == @note }
- #end
+ it_should_be_valid_event
+ it { @event.action.should == Event::Commented }
+ it { @event.target.should == @note }
+ end
end
diff --git a/spec/observers/issue_observer_spec.rb b/spec/observers/issue_observer_spec.rb
index 509c1d02..bbffbd34 100644
--- a/spec/observers/issue_observer_spec.rb
+++ b/spec/observers/issue_observer_spec.rb
@@ -85,7 +85,7 @@ describe IssueObserver do
it 'notification is delivered if the issue being closed' do
issue.stub(:is_being_closed?).and_return(true)
- Notify.should_receive(:issue_status_changed_email).twice
+ Notify.should_receive(:issue_status_changed_email).twice.and_return(stub(deliver: true))
Note.should_receive(:create_status_change_note).with(issue, some_user, 'closed')
subject.after_update(issue)
@@ -104,7 +104,7 @@ describe IssueObserver do
issue_without_assignee.stub(:is_being_reassigned?).and_return(false)
issue_without_assignee.stub(:is_being_closed?).and_return(true)
issue_without_assignee.stub(:is_being_reopened?).and_return(false)
- Notify.should_receive(:issue_status_changed_email).once
+ Notify.should_receive(:issue_status_changed_email).once.and_return(stub(deliver: true))
Note.should_receive(:create_status_change_note).with(issue_without_assignee, some_user, 'closed')
subject.after_update(issue_without_assignee)
@@ -128,7 +128,7 @@ describe IssueObserver do
it 'notification is delivered if the issue being reopened' do
issue.stub(:is_being_reopened?).and_return(true)
- Notify.should_receive(:issue_status_changed_email).twice
+ Notify.should_receive(:issue_status_changed_email).twice.and_return(stub(deliver: true))
Note.should_receive(:create_status_change_note).with(issue, some_user, 'reopened')
subject.after_update(issue)
@@ -147,7 +147,7 @@ describe IssueObserver do
issue_without_assignee.stub(:is_being_reassigned?).and_return(false)
issue_without_assignee.stub(:is_being_closed?).and_return(false)
issue_without_assignee.stub(:is_being_reopened?).and_return(true)
- Notify.should_receive(:issue_status_changed_email).once
+ Notify.should_receive(:issue_status_changed_email).once.and_return(stub(deliver: true))
Note.should_receive(:create_status_change_note).with(issue_without_assignee, some_user, 'reopened')
subject.after_update(issue_without_assignee)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index b4e2fbbd..a3965164 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -117,6 +117,14 @@ describe Gitlab::API do
json_response.count.should == 2
json_response.first['email'].should == user.email
end
+
+ it "finds team members with query string" do
+ get api("/projects/#{project.path}/members", user), query: user.username
+ response.status.should == 200
+ json_response.should be_an Array
+ json_response.count.should == 1
+ json_response.first['email'].should == user.email
+ end
end
describe "GET /projects/:id/members/:user_id" do
diff --git a/spec/requests/gitlab_flavored_markdown_spec.rb b/spec/requests/gitlab_flavored_markdown_spec.rb
index ad5d7cd7..7f61c6aa 100644
--- a/spec/requests/gitlab_flavored_markdown_spec.rb
+++ b/spec/requests/gitlab_flavored_markdown_spec.rb
@@ -6,7 +6,7 @@ describe "Gitlab Flavored Markdown" do
let(:merge_request) { create(:merge_request, project: project) }
let(:fred) do
u = create(:user, name: "fred")
- project.users << u
+ project.add_access(u, :admin)
u
end
@@ -19,7 +19,7 @@ describe "Gitlab Flavored Markdown" do
@test_file = "gfm_test_file"
i.add(@test_file, "foo\nbar\n")
# add commit with gfm
- i.commit("fix ##{issue.id}\n\nask @#{fred.name} for details", head: @branch_name)
+ i.commit("fix ##{issue.id}\n\nask @#{fred.username} for details", head: @branch_name)
# add test tag
@tag_name = "gfm-test-tag"
@@ -56,7 +56,7 @@ describe "Gitlab Flavored Markdown" do
it "should render description in commits#show" do
visit project_commit_path(project, commit)
- page.should have_link("@#{fred.name}")
+ page.should have_link("@#{fred.username}")
end
it "should render title in refs#tree", js: true do
@@ -93,7 +93,7 @@ describe "Gitlab Flavored Markdown" do
assignee: @user,
project: project,
title: "fix ##{@other_issue.id}",
- description: "ask @#{fred.name} for details")
+ description: "ask @#{fred.username} for details")
end
it "should render subject in issues#index" do
@@ -111,7 +111,7 @@ describe "Gitlab Flavored Markdown" do
it "should render details in issues#show" do
visit project_issue_path(project, @issue)
- page.should have_link("@#{fred.name}")
+ page.should have_link("@#{fred.username}")
end
end
@@ -142,7 +142,7 @@ describe "Gitlab Flavored Markdown" do
@milestone = create(:milestone,
project: project,
title: "fix ##{issue.id}",
- description: "ask @#{fred.name} for details")
+ description: "ask @#{fred.username} for details")
end
it "should render title in milestones#index" do
@@ -160,7 +160,7 @@ describe "Gitlab Flavored Markdown" do
it "should render description in milestones#show" do
visit project_milestone_path(project, @milestone)
- page.should have_link("@#{fred.name}")
+ page.should have_link("@#{fred.username}")
end
end
diff --git a/spec/requests/issues_spec.rb b/spec/requests/issues_spec.rb
index a4b02686..08141085 100644
--- a/spec/requests/issues_spec.rb
+++ b/spec/requests/issues_spec.rb
@@ -11,7 +11,7 @@ describe "Issues" do
project.add_access(user2, :read, :write)
end
- describe "Edit issue", js: true do
+ describe "Edit issue" do
let!(:issue) do
create(:issue,
author: @user,
@@ -91,13 +91,13 @@ describe "Issues" do
title: title)
end
- issue = Issue.first # with title 'foobar'
- issue.milestone = create(:milestone, project: project)
- issue.assignee = nil
- issue.save
+ @issue = Issue.first # with title 'foobar'
+ @issue.milestone = create(:milestone, project: project)
+ @issue.assignee = nil
+ @issue.save
end
- let(:issue) { Issue.first }
+ let(:issue) { @issue }
it "should allow filtering by issues with no specified milestone" do
visit project_issues_path(project, milestone_id: '0')
diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb
index 8c0f8e5f..e097f080 100644
--- a/spec/requests/projects_spec.rb
+++ b/spec/requests/projects_spec.rb
@@ -58,7 +58,7 @@ describe "Projects" do
describe "DELETE /projects/:id" do
before do
- @project = create(:project)
+ @project = create(:project, owner: @user)
@project.add_access(@user, :read, :admin)
visit edit_project_path(@project)
end
diff --git a/spec/requests/snippets_spec.rb b/spec/requests/snippets_spec.rb
index 9ef217ba..b231b940 100644
--- a/spec/requests/snippets_spec.rb
+++ b/spec/requests/snippets_spec.rb
@@ -48,11 +48,11 @@ describe "Snippets" do
page.current_path.should == new_project_snippet_path(project)
end
- describe "fill in" do
+ describe "fill in", js: true do
before do
fill_in "snippet_title", with: "login function"
fill_in "snippet_file_name", with: "test.rb"
- fill_in "snippet_content", with: "def login; end"
+ page.execute_script("editor.insert('def login; end');")
end
it { expect { click_button "Save" }.to change {Snippet.count}.by(1) }
@@ -83,7 +83,6 @@ describe "Snippets" do
before do
fill_in "snippet_title", with: "login function"
fill_in "snippet_file_name", with: "test.rb"
- fill_in "snippet_content", with: "def login; end"
end
it { expect { click_button "Save" }.to_not change {Snippet.count} }
diff --git a/spec/roles/account_role_spec.rb b/spec/roles/account_role_spec.rb
new file mode 100644
index 00000000..4b214551
--- /dev/null
+++ b/spec/roles/account_role_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe User, "Account" do
+ describe 'normal user' do
+ let(:user) { create(:user, name: 'John Smith') }
+
+ it { user.is_admin?.should be_false }
+ it { user.require_ssh_key?.should be_true }
+ it { user.can_create_group?.should be_false }
+ it { user.can_create_project?.should be_true }
+ it { user.first_name.should == 'John' }
+ end
+
+ describe 'blocking user' do
+ let(:user) { create(:user, name: 'John Smith') }
+
+ it "should block user" do
+ user.block
+ user.blocked.should be_true
+ end
+ end
+
+ describe 'projects' do
+ before do
+ ActiveRecord::Base.observers.enable(:user_observer)
+ @user = create :user
+ @project = create :project, namespace: @user.namespace
+ end
+
+ it { @user.authorized_projects.should include(@project) }
+ it { @user.my_own_projects.should include(@project) }
+ end
+
+ describe 'namespaced' do
+ before do
+ ActiveRecord::Base.observers.enable(:user_observer)
+ @user = create :user
+ @project = create :project, namespace: @user.namespace
+ end
+
+ it { @user.several_namespaces?.should be_false }
+ it { @user.namespaces.should == [@user.namespace] }
+ end
+end
diff --git a/spec/roles/repository_spec.rb b/spec/roles/repository_spec.rb
index 3507585a..e1d01cbf 100644
--- a/spec/roles/repository_spec.rb
+++ b/spec/roles/repository_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Project, "Repository" do
- let(:project) { build(:project) }
+ let(:project) { create(:project) }
describe "#empty_repo?" do
it "should return true if the repo doesn't exist" do
@@ -69,4 +69,91 @@ describe Project, "Repository" do
project.root_ref?('stable').should be_false
end
end
+
+ describe :repo do
+ it "should return valid repo" do
+ project.repo.should be_kind_of(Grit::Repo)
+ end
+
+ it "should return nil" do
+ lambda { Project.new(path: "invalid").repo }.should raise_error(Grit::NoSuchPathError)
+ end
+
+ it "should return nil" do
+ lambda { Project.new.repo }.should raise_error(TypeError)
+ end
+ end
+
+ describe :commit do
+ it "should return first head commit if without params" do
+ project.commit.id.should == project.repo.commits.first.id
+ end
+
+ it "should return valid commit" do
+ project.commit(ValidCommit::ID).should be_valid_commit
+ end
+
+ it "should return nil" do
+ project.commit("+123_4532530XYZ").should be_nil
+ end
+ end
+
+ describe :tree do
+ before do
+ @commit = project.commit(ValidCommit::ID)
+ end
+
+ it "should raise error w/o arguments" do
+ lambda { project.tree }.should raise_error
+ end
+
+ it "should return root tree for commit" do
+ tree = project.tree(@commit)
+ tree.contents.size.should == ValidCommit::FILES_COUNT
+ tree.contents.map(&:name).should == ValidCommit::FILES
+ end
+
+ it "should return root tree for commit with correct path" do
+ tree = project.tree(@commit, ValidCommit::C_FILE_PATH)
+ tree.contents.map(&:name).should == ValidCommit::C_FILES
+ end
+
+ it "should return root tree for commit with incorrect path" do
+ project.tree(@commit, "invalid_path").should be_nil
+ end
+ end
+
+ describe "fresh commits" do
+ let(:project) { create(:project) }
+
+ it { project.fresh_commits(3).count.should == 3 }
+ it { project.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" }
+ it { project.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" }
+ end
+
+ describe "commits_between" do
+ let(:project) { create(:project) }
+
+ subject do
+ commits = project.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff",
+ "8470d70da67355c9c009e4401746b1d5410af2e3")
+ commits.map { |c| c.id }
+ end
+
+ it { should have(3).elements }
+ it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") }
+ it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }
+ end
+
+ describe :valid_repo? do
+ it "should be valid repo" do
+ project = create(:project)
+ project.valid_repo?.should be_true
+ end
+
+ it "should be invalid repo" do
+ project = Project.new(name: "ok_name", path: "/INVALID_PATH/", path: "NEOK")
+ project.valid_repo?.should be_false
+ end
+ end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 25db2f91..09e11588 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -245,6 +245,7 @@ describe MergeRequestsController, "routing" do
it_behaves_like "RESTful project resources" do
let(:controller) { 'merge_requests' }
+ let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
end
@@ -325,6 +326,7 @@ end
describe MilestonesController, "routing" do
it_behaves_like "RESTful project resources" do
let(:controller) { 'milestones' }
+ let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
end
@@ -360,6 +362,7 @@ describe IssuesController, "routing" do
it_behaves_like "RESTful project resources" do
let(:controller) { 'issues' }
+ let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 988063db..57fd70e7 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -33,6 +33,7 @@ end
# help_system_hooks GET /help/system_hooks(.:format) help#system_hooks
# help_markdown GET /help/markdown(.:format) help#markdown
# help_ssh GET /help/ssh(.:format) help#ssh
+# help_raketasks GET /help/raketasks(.:format) help#raketasks
describe HelpController, "routing" do
it "to #index" do
get("/help").should route_to('help#index')
@@ -65,6 +66,10 @@ describe HelpController, "routing" do
it "to #ssh" do
get("/help/ssh").should route_to('help#ssh')
end
+
+ it "to #raketasks" do
+ get("/help/raketasks").should route_to('help#raketasks')
+ end
end
# errors_githost GET /errors/githost(.:format) errors#githost
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 7728b1e9..9f066c0e 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -42,8 +42,8 @@ RSpec.configure do |config|
# ActiveRecord::Base.observers.enable(:all)
# Use tmp dir for FS manipulations
- Gitlab.config.stub(git_base_path: Rails.root.join('tmp', 'test-git-base-path'))
- FileUtils.rm_rf Gitlab.config.git_base_path
- FileUtils.mkdir_p Gitlab.config.git_base_path
+ Gitlab.config.gitolite.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
+ FileUtils.rm_rf Gitlab.config.gitolite.repos_path
+ FileUtils.mkdir_p Gitlab.config.gitolite.repos_path
end
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index d9aa0543..b17521e0 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -15,8 +15,8 @@ describe 'gitlab:app namespace rake task' do
end
let :run_rake_task do
- Rake::Task["gitlab:app:backup_restore"].reenable
- Rake.application.invoke_task "gitlab:app:backup_restore"
+ Rake::Task["gitlab:backup:restore"].reenable
+ Rake.application.invoke_task "gitlab:backup:restore"
end
context 'gitlab version' do
@@ -36,8 +36,8 @@ describe 'gitlab:app namespace rake task' do
it 'should invoke restoration on mach' do
YAML.stub :load_file => {:gitlab_version => gitlab_version}
- Rake::Task["gitlab:app:db_restore"].should_receive :invoke
- Rake::Task["gitlab:app:repo_restore"].should_receive :invoke
+ Rake::Task["gitlab:backup:db:restore"].should_receive :invoke
+ Rake::Task["gitlab:backup:repo:restore"].should_receive :invoke
expect { run_rake_task }.to_not raise_error SystemExit
end
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index bbc91f44..26b461c3 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -14,8 +14,8 @@ describe PostReceive do
let(:key_id) { key.identifier }
it "fetches the correct project" do
- Project.should_receive(:find_by_path).with(project.path).and_return(project)
- PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id)
+ Project.should_receive(:find_with_namespace).with(project.path_with_namespace).and_return(project)
+ PostReceive.perform(pwd(project), 'sha-old', 'sha-new', 'refs/heads/master', key_id)
end
it "does not run if the author is not in the project" do
@@ -24,17 +24,21 @@ describe PostReceive do
project.should_not_receive(:observe_push)
project.should_not_receive(:execute_hooks)
- PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id).should be_false
+ PostReceive.perform(pwd(project), 'sha-old', 'sha-new', 'refs/heads/master', key_id).should be_false
end
it "asks the project to trigger all hooks" do
- Project.stub(find_by_path: project)
+ Project.stub(find_with_namespace: project)
project.should_receive(:execute_hooks)
project.should_receive(:execute_services)
project.should_receive(:update_merge_requests)
project.should_receive(:observe_push)
- PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id)
+ PostReceive.perform(pwd(project), 'sha-old', 'sha-new', 'refs/heads/master', key_id)
end
end
+
+ def pwd(project)
+ File.join(Gitlab.config.gitolite.repos_path, project.path_with_namespace)
+ end
end
diff --git a/vendor/assets/javascripts/branch-graph.js b/vendor/assets/javascripts/branch-graph.js
index e8699bdf..a7e1e152 100644
--- a/vendor/assets/javascripts/branch-graph.js
+++ b/vendor/assets/javascripts/branch-graph.js
@@ -1,181 +1,255 @@
-var commits = {},
- comms = {},
- pixelsX = [],
- pixelsY = [],
- mmax = Math.max,
- mtime = 0,
- mspace = 0,
- parents = {},
- ii = 0,
- colors = ["#000"];
+!function(){
-function initGraph(){
- commits = chunk1.commits;
- ii = commits.length;
- for (var i = 0; i < ii; i++) {
- for (var j = 0, jj = commits[i].parents.length; j < jj; j++) {
- parents[commits[i].parents[j][0]] = true;
+ var BranchGraph = function(element, options){
+ this.element = element;
+ this.options = options;
+
+ this.preparedCommits = {};
+ this.mtime = 0;
+ this.mspace = 0;
+ this.parents = {};
+ this.colors = ["#000"];
+
+ this.load();
+ };
+
+ BranchGraph.prototype.load = function(){
+ $.ajax({
+ url: this.options.url,
+ method: 'get',
+ dataType: 'json',
+ success: $.proxy(function(data){
+ $('.loading', this.element).hide();
+ this.prepareData(data.days, data.commits);
+ this.buildGraph();
+ }, this)
+ });
+ };
+
+ BranchGraph.prototype.prepareData = function(days, commits){
+ this.days = days;
+ this.dayCount = days.length;
+ this.commits = commits;
+ this.commitCount = commits.length;
+
+ this.collectParents();
+
+ this.mtime += 4;
+ this.mspace += 10;
+ for (var i = 0; i < this.commitCount; i++) {
+ if (this.commits[i].id in this.parents) {
+ this.commits[i].isParent = true;
}
- mtime = Math.max(mtime, commits[i].time);
- mspace = Math.max(mspace, commits[i].space);
- }
- mtime = mtime + 4;
- mspace = mspace + 10;
- for (i = 0; i < ii; i++) {
- if (commits[i].id in parents) {
- commits[i].isParent = true;
- }
- comms[commits[i].id] = commits[i];
- }
- for (var k = 0; k < mspace; k++) {
- colors.push(Raphael.getColor());
- }
-}
-
-function branchGraph(holder) {
- var ch = mspace * 20 + 20, cw = mtime * 20 + 20,
- r = Raphael("holder", cw, ch),
- top = r.set();
- var cuday = 0, cumonth = "";
- r.rect(0, 0, days.length * 20 + 80, 30).attr({fill: "#222"});
- r.rect(0, 30, days.length * 20 + 80, 20).attr({fill: "#444"});
-
- for (mm = 0; mm < days.length; mm++) {
- if(days[mm] != null){
- if(cuday != days[mm][0]){
- r.text(10 + mm * 20, 40, days[mm][0]).attr({font: "14px Fontin-Sans, Arial", fill: "#DDD"});
- cuday = days[mm][0]
- }
- if(cumonth != days[mm][1]){
- r.text(10 + mm * 20, 15, days[mm][1]).attr({font: "14px Fontin-Sans, Arial", fill: "#EEE"});
- cumonth = days[mm][1]
- }
-
- }
+ this.preparedCommits[this.commits[i].id] = this.commits[i];
}
- for (i = 0; i < ii; i++) {
- var x = 10 + 20 * commits[i].time,
- y = 70 + 20 * commits[i].space;
- r.circle(x, y, 3).attr({fill: colors[commits[i].space], stroke: "none"});
- if (commits[i].refs != null && commits[i].refs != "") {
- var longrefs = commits[i].refs
- var shortrefs = commits[i].refs;
- if (shortrefs.length > 15){
- shortrefs = shortrefs.substr(0,13) + "...";
- }
- var t = r.text(x+5, y+5, shortrefs).attr({font: "12px Fontin-Sans, Arial", fill: "#666",
- title: longrefs, cursor: "pointer", rotation: "90"});
+ this.collectColors();
+ };
+
+ BranchGraph.prototype.collectParents = function(){
+ for (var i = 0; i < this.commitCount; i++) {
+ for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
+ this.parents[this.commits[i].parents[j][0]] = true;
+ }
+ this.mtime = Math.max(this.mtime, this.commits[i].time);
+ this.mspace = Math.max(this.mspace, this.commits[i].space);
+ }
+ };
+
+ BranchGraph.prototype.collectColors = function(){
+ for (var k = 0; k < this.mspace; k++) {
+ this.colors.push(Raphael.getColor());
+ }
+ };
- var textbox = t.getBBox();
- t.translate(textbox.height/-4,textbox.width/2);
+ BranchGraph.prototype.buildGraph = function(){
+ var graphWidth = $(this.element).width()
+ , ch = this.mspace * 20 + 20
+ , cw = Math.max(graphWidth, this.mtime * 20 + 20)
+ , r = Raphael(this.element.get(0), cw, ch)
+ , top = r.set()
+ , cuday = 0
+ , cumonth = ""
+ , offsetX = 20
+ , offsetY = 60
+ , barWidth = Math.max(graphWidth, this.dayCount * 20 + 80);
+
+ this.raphael = r;
+
+ r.rect(0, 0, barWidth, 20).attr({fill: "#222"});
+ r.rect(0, 20, barWidth, 20).attr({fill: "#444"});
+
+ for (mm = 0; mm < this.dayCount; mm++) {
+ if(this.days[mm] != null){
+ if(cuday != this.days[mm][0]){
+ // Dates
+ r.text(offsetX + mm * 20, 31, this.days[mm][0]).attr({
+ font: "12px Monaco, Arial",
+ fill: "#DDD"
+ });
+ cuday = this.days[mm][0];
}
- for (var j = 0, jj = commits[i].parents.length; j < jj; j++) {
- var c = comms[commits[i].parents[j][0]];
- if (c) {
- var cx = 10 + 20 * c.time,
- cy = 70 + 20 * c.space;
- if (c.space == commits[i].space) {
- r.path("M" + (x - 5) + "," + (y + .0001) + "L" + (15 + 20 * c.time) + "," + (y + .0001))
- .attr({stroke: colors[c.space], "stroke-width": 2});
+ if(cumonth != this.days[mm][1]){
+ // Months
+ r.text(offsetX + mm * 20, 11, this.days[mm][1]).attr({
+ font: "12px Monaco, Arial",
+ fill: "#EEE"
+ });
+ cumonth = this.days[mm][1];
+ }
+ }
+ }
+
+ for (i = 0; i < this.commitCount; i++) {
+ var x = offsetX + 20 * this.commits[i].time
+ , y = offsetY + 20 * this.commits[i].space;
+ r.circle(x, y, 3).attr({
+ fill: this.colors[this.commits[i].space],
+ stroke: "none"
+ });
+ if (this.commits[i].refs != null && this.commits[i].refs != "") {
+ var longrefs = this.commits[i].refs
+ , shortrefs = this.commits[i].refs;
+ if (shortrefs.length > 15){
+ shortrefs = shortrefs.substr(0,13) + "...";
+ }
+ var t = r.text(x+5, y+8, shortrefs).attr({
+ font: "12px Monaco, Arial",
+ fill: "#666",
+ title: longrefs,
+ cursor: "pointer",
+ rotation: "90"
+ });
- } else if (c.space < commits[i].space) {
- r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C", x - 5, y, x - 17, y + 2, x - 20, y - 5, "L", cx, y - 5, cx, cy])
- .attr({stroke: colors[commits[i].space], "stroke-width": 2});
- } else {
- r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5L", x - 10, y + 20, "L", x - 10, cy, cx, cy])
- .attr({stroke: colors[c.space], "stroke-width": 2});
- }
- }
+ var textbox = t.getBBox();
+ t.translate(textbox.height/-4, textbox.width/2);
+ }
+ var c;
+ for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
+ c = this.preparedCommits[this.commits[i].parents[j][0]];
+ if (c) {
+ var cx = offsetX + 20 * c.time
+ , cy = offsetY + 20 * c.space;
+ if (c.space == this.commits[i].space) {
+ r.path([
+ "M", x, y,
+ "L", x - 20 * (c.time + 1), y
+ ]).attr({
+ stroke: this.colors[c.space],
+ "stroke-width": 2
+ });
+
+ } else if (c.space < this.commits[i].space) {
+ r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C", x - 5, y, x - 17, y + 2, x - 20, y - 5, "L", cx, y - 5, cx, cy])
+ .attr({
+ stroke: this.colors[this.commits[i].space],
+ "stroke-width": 2
+ });
+ } else {
+ r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5L", x - 10, y + 20, "L", x - 10, cy, cx, cy])
+ .attr({
+ stroke: this.colors[c.space],
+ "stroke-width": 2
+ });
+ }
}
- (function (c, x, y) {
- top.push(r.circle(x, y, 10).attr({fill: "#000", opacity: 0, cursor: "pointer"})
- .click(function(){
- location.href = location.href.replace("graph", "commits/" + c.id);
- })
- .hover(function () {
- var s = r.text(100, 100,c.author + "\n \n" +c.id + "\n \n" + c.message).attr({fill: "#fff"});
- this.popup = r.popupit(x, y + 5, s, 0);
- top.push(this.popup.insertBefore(this));
- }, function () {
- this.popup && this.popup.remove() && delete this.popup;
- }));
- }(commits[i], x, y));
+ }
+ this.appendAnchor(top, this.commits[i], x, y);
}
top.toFront();
- var hw = holder.offsetWidth,
- hh = holder.offsetHeight,
- v = r.rect(hw - 8, 0, 4, Math.pow(hh, 2) / ch, 2).attr({fill: "#000", opacity: 0}),
- h = r.rect(0, hh - 8, Math.pow(hw, 2) / cw, 4, 2).attr({fill: "#000", opacity: 0}),
- bars = r.set(v, h),
- drag,
- dragger = function (e) {
- if (drag) {
- e = e || window.event;
- holder.scrollLeft = drag.sl - (e.clientX - drag.x);
- holder.scrollTop = drag.st - (e.clientY - drag.y);
- }
+ this.element.scrollLeft(cw);
+ this.bindEvents();
+ };
+
+ BranchGraph.prototype.bindEvents = function(){
+ var drag = {}
+ , element = this.element;
+
+ var dragger = function(event){
+ element.scrollLeft(drag.sl - (event.clientX - drag.x));
+ element.scrollTop(drag.st - (event.clientY - drag.y));
+ };
+
+ element.on({
+ mousedown: function (event) {
+ drag = {
+ x: event.clientX,
+ y: event.clientY,
+ st: element.scrollTop(),
+ sl: element.scrollLeft()
};
- holder.onmousedown = function (e) {
- e = e || window.event;
- drag = {x: e.clientX, y: e.clientY, st: holder.scrollTop, sl: holder.scrollLeft};
- document.onmousemove = dragger;
- bars.animate({opacity: .5}, 300);
- };
- document.onmouseup = function () {
- drag = false;
- document.onmousemove = null;
- bars.animate({opacity: 0}, 300);
- };
- holder.scrollLeft = cw;
-};
-Raphael.fn.popupit = function (x, y, set, dir, size) {
- dir = dir == null ? 2 : dir;
- size = size || 5;
- x = Math.round(x);
- y = Math.round(y);
- var bb = set.getBBox(),
- w = Math.round(bb.width / 2),
- h = Math.round(bb.height / 2),
- dx = [0, w + size * 2, 0, -w - size * 2],
- dy = [-h * 2 - size * 3, -h - size, 0, -h - size],
- p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size,
- "l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size,
- "l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size,
- "l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size,
- "l", -mmax(w - size, 0), 0, "z"].join(","),
- xy = [{x: x, y: y + size * 2 + h}, {x: x - size * 2 - w, y: y}, {x: x, y: y - size * 2 - h}, {x: x + size * 2 + w, y: y}][dir];
- set.translate(xy.x - w - bb.x, xy.y - h - bb.y);
- return this.set(this.path(p).attr({fill: "#234", stroke: "none"}).insertBefore(set.node ? set : set[0]), set);
-};
-Raphael.fn.popup = function (x, y, text, dir, size) {
- dir = dir == null ? 2 : dir > 3 ? 3 : dir;
- size = size || 5;
- text = text || "$9.99";
- var res = this.set(),
- d = 3;
- res.push(this.path().attr({fill: "#000", stroke: "#000"}));
- res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff", "font-family": "Helvetica, Arial"}));
- res.update = function (X, Y, withAnimation) {
- X = X || x;
- Y = Y || y;
- var bb = this[1].getBBox(),
- w = bb.width / 2,
- h = bb.height / 2,
- dx = [0, w + size * 2, 0, -w - size * 2],
- dy = [-h * 2 - size * 3, -h - size, 0, -h - size],
- p = ["M", X - dx[dir], Y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size,
- "l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size,
- "l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size,
- "l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size,
- "l", -mmax(w - size, 0), 0, "z"].join(","),
- xy = [{x: X, y: Y + size * 2 + h}, {x: X - size * 2 - w, y: Y}, {x: X, y: Y - size * 2 - h}, {x: X + size * 2 + w, y: Y}][dir];
- xy.path = p;
- if (withAnimation) {
- this.animate(xy, 500, ">");
- } else {
- this.attr(xy);
+ $(window).on('mousemove', dragger);
+ }
+ });
+ $(window).on({
+ mouseup: function(){
+ //bars.animate({opacity: 0}, 300);
+ $(window).off('mousemove', dragger);
+ },
+ keydown: function(event){
+ if(event.keyCode == 37){
+ // left
+ element.scrollLeft( element.scrollLeft() - 50);
}
- return this;
- };
- return res.update(x, y);
+ if(event.keyCode == 38){
+ // top
+ element.scrollTop( element.scrollTop() - 50);
+ }
+ if(event.keyCode == 39){
+ // right
+ element.scrollLeft( element.scrollLeft() + 50);
+ }
+ if(event.keyCode == 40){
+ // bottom
+ element.scrollTop( element.scrollTop() + 50);
+ }
+ }
+ });
+ };
+
+ BranchGraph.prototype.appendAnchor = function(top, c, x, y) {
+ var r = this.raphael
+ , options = this.options
+ , anchor;
+ anchor = r.circle(x, y, 10).attr({
+ fill: "#000",
+ opacity: 0,
+ cursor: "pointer"
+ })
+ .click(function(){
+ window.location = options.commit_url.replace('%s', c.id);
+ })
+ .hover(function(){
+ var text = r.text(100, 100, c.author + "\n \n" + c.id + "\n \n" + c.message).attr({
+ fill: "#fff"
+ });
+ this.popup = r.tooltip(x, y + 5, text, 0);
+ top.push(this.popup.insertBefore(this));
+ }, function(){
+ this.popup && this.popup.remove() && delete this.popup;
+ });
+ top.push(anchor);
+ };
+
+ this.BranchGraph = BranchGraph;
+
+}(this);
+Raphael.fn.tooltip = function (x, y, set, dir, size) {
+ dir = dir == null ? 2 : dir;
+ size = size || 5;
+ x = Math.round(x);
+ y = Math.round(y);
+ var mmax = Math.max
+ , bb = set.getBBox()
+ , w = Math.round(bb.width / 2)
+ , h = Math.round(bb.height / 2)
+ , dx = [0, w + size * 2, 0, -w - size * 2]
+ , dy = [-h * 2 - size * 3, -h - size, 0, -h - size]
+ , p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size,
+ "l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size,
+ "l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size,
+ "l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size,
+ "l", -mmax(w - size, 0), 0, "z"].join(",")
+ , xy = [{x: x, y: y + size * 2 + h}, {x: x - size * 2 - w, y: y}, {x: x, y: y - size * 2 - h}, {x: x + size * 2 + w, y: y}][dir];
+ set.translate(xy.x - w - bb.x, xy.y - h - bb.y);
+ return this.set(this.path(p).attr({fill: "#234", stroke: "none"}).insertBefore(set.node ? set : set[0]), set);
};