Commit 278935a3 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'release/6-3' of /home/git/repositories/gitlab/gitlab-ee

parents 6d688217 4e77b0b5
language: ruby
env:
- DB=mysql TRAVIS=true
global:
- DB=mysql
- TRAVIS=true
matrix:
- TASK=spinach
- TASK=spec
- TASK=jasmine:ci
before_install:
- sudo apt-get install libicu-dev -y
- gem install charlock_holmes -v="0.6.9"
branches:
only:
- 'master'
......@@ -11,8 +16,9 @@ rvm:
- 2.0.0
services:
- mysql
- postgresql
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
script: "bundle exec rake gitlab:test --trace"
- "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
script: "bundle exec rake $TASK --trace"
v 6.3.0
- API for adding gitlab-ci service
- Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
- Restyle project home page
- Grammar fixes
- Show branches list (which branches contains commit) on commit page (Andrew Kumanyaev)
- Security improvements
- Added support for GitLab CI 4.0
- Fixed issue with 500 error when group did not exist
- Ability to leave project
- You can create file in repo using UI
- You can remove file from repo using UI
- API: dropped default_branch attribute from project during creation
- Project default_branch is not stored in db any more. It takes from repo now.
- Admin broadcast messages
- UI improvements
- Dont show last push widget if user removed this branch
- Fix 500 error for repos with newline in file name
- Extended html titles
- API: create/update/delete repo files
- Admin can transfer project to any namespace
- API: projects/all for admin users
- Fix recent branches order
v 6.2.4
- Security: Cast API private_token to string (CVE-2013-4580)
- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
- Fix for Git SSH access for LDAP users
v 6.2.3
- Security: More protection against CVE-2013-4489
- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
- Fix sidekiq rake tasks
v 6.2.2
- Security: Update gitlab_git (CVE-2013-4489)
v 6.2.1
- Security: Fix issue with generated passwords for new users
v 6.2.0
- Public project pages are now visible to everyone (files, issues, wik, etc.)
THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
......@@ -15,7 +55,7 @@ v 6.2.0
- Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
- API: Remove group
- API: Remove project
- Avatar upload on profile page with a maximum of 200KB (Steven Thonus)
- Avatar upload on profile page with a maximum of 100KB (Steven Thonus)
- Store the sessions in Redis instead of the cookie store
- Fixed relative links in markdown
- User must confirm his email if signup enabled
......@@ -83,6 +123,14 @@ v 6.0.0
- Improved MR comments logic
- Render readme file for projects in public area
v 5.4.2
- Security: Cast API private_token to string (CVE-2013-4580)
- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
v 5.4.1
- Security: Fixes for CVE-2013-4489
- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
v 5.4.0
- Ability to edit own comments
- Documentation improvements
......
......@@ -5,9 +5,18 @@ This guide details how to use issues and pull requests to improve GitLab.
- [Closing policy for issues and pull requests](#closing-policy-for-issues-and-pull-requests)
- [Issue tracker](#issue-tracker)
- [Pull requests](#pull-requests)
- [Security vulnerabilities](#security-vulnerabilities)
If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md).
## Contributor license agreement
By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
## Security vulnerability disclosure
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
## Closing policy for issues and pull requests
GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice.
......
......@@ -8,7 +8,7 @@ def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem "rails", "3.2.13"
gem "rails", "3.2.15"
# Supported DBs
gem "mysql2", group: :mysql
......
GEM
remote: https://rubygems.org/
specs:
actionmailer (3.2.13)
actionpack (= 3.2.13)
mail (~> 2.5.3)
actionpack (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
actionmailer (3.2.15)
actionpack (= 3.2.15)
mail (~> 2.5.4)
actionpack (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
......@@ -14,19 +14,19 @@ GEM
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.13)
activesupport (= 3.2.13)
activemodel (3.2.15)
activesupport (= 3.2.15)
builder (~> 3.0.0)
activerecord (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
activerecord (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
activesupport (3.2.13)
i18n (= 0.6.1)
activeresource (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
activesupport (3.2.15)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
acts-as-taggable-on (2.4.1)
rails (>= 3, < 5)
......@@ -34,7 +34,7 @@ GEM
annotate (2.6.0.beta2)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
arel (3.0.2)
arel (3.0.3)
asciidoctor (0.1.3)
awesome_print (1.2.0)
backports (3.3.2)
......@@ -108,7 +108,7 @@ GEM
warden (~> 1.2.1)
devise-async (0.8.0)
devise (>= 2.2, < 3.2)
diff-lcs (1.2.4)
diff-lcs (1.2.5)
dotenv (0.8.0)
email_spec (1.4.0)
launchy (~> 2.1)
......@@ -171,7 +171,7 @@ GEM
stringex (~> 1.5.1)
gitlab-grack (1.0.1)
rack (~> 1.4.1)
gitlab-grit (2.6.1)
gitlab-grit (2.6.3)
charlock_holmes (~> 0.6.9)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
......@@ -235,7 +235,7 @@ GEM
multi_json (~> 1.0)
multi_xml (>= 0.5.2)
httpauth (0.2.0)
i18n (0.6.1)
i18n (0.6.5)
jasmine (1.3.2)
jasmine-core (~> 1.3.1)
rack (~> 1.0)
......@@ -274,11 +274,11 @@ GEM
mime-types (~> 1.16)
treetop (~> 1.4.8)
method_source (0.8.1)
mime-types (1.25)
mime-types (1.25.1)
minitest (4.7.4)
modernizr (2.6.2)
sprockets (~> 2.0)
multi_json (1.8.0)
multi_json (1.8.2)
multi_xml (0.5.4)
multipart-post (1.2.0)
mysql2 (0.3.11)
......@@ -348,14 +348,14 @@ GEM
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (3.2.13)
actionmailer (= 3.2.13)
actionpack (= 3.2.13)
activerecord (= 3.2.13)
activeresource (= 3.2.13)
activesupport (= 3.2.13)
rails (3.2.15)
actionmailer (= 3.2.15)
actionpack (= 3.2.15)
activerecord (= 3.2.15)
activeresource (= 3.2.15)
activesupport (= 3.2.15)
bundler (~> 1.0)
railties (= 3.2.13)
railties (= 3.2.15)
rails-dev-tweaks (0.6.1)
actionpack (~> 3.1)
railties (~> 3.1)
......@@ -368,9 +368,9 @@ GEM
i18n
require_all
ruby-progressbar
railties (3.2.13)
actionpack (= 3.2.13)
activesupport (= 3.2.13)
railties (3.2.15)
actionpack (= 3.2.15)
activesupport (= 3.2.15)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
......@@ -514,7 +514,7 @@ GEM
multi_json (~> 1.5)
twitter-stream (~> 0.1)
tins (0.11.0)
treetop (1.4.14)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
turbolinks (1.2.0)
......@@ -523,7 +523,7 @@ GEM
eventmachine (>= 0.12.8)
http_parser.rb (~> 0.5.1)
simple_oauth (~> 0.1.4)
tzinfo (0.3.37)
tzinfo (0.3.38)
uglifier (2.1.1)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
......@@ -615,7 +615,7 @@ DEPENDENCIES
quiet_assets (~> 1.0.1)
rack-attack
rack-mini-profiler
rails (= 3.2.13)
rails (= 3.2.15)
rails-dev-tweaks
rails_best_practices
raphael-rails (~> 2.1.2)
......
......@@ -64,9 +64,10 @@ If you want to contribute, please first read our [Contributing Guidelines](https
* [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems.
* [BitNami one-click installers](http://bitnami.com/stack/gitlab)
* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab)
* [Digital Ocean 1-Click Application Install](https://www.digitalocean.com/) Have a new server up in 55 seconds. Digital Ocean uses SSD disks which is great for an IO intensive app as GitLab. Look for GitLab under 'Select Image' => 'Applications' when creating a droplet.
* [BitNami one-click installers](http://bitnami.com/stack/gitlab) Get an image with GitLab and GitLab CI preinstalled for Amazon Web Services, Azure, VMware or your local server.
### New versions and upgrading
......@@ -99,7 +100,7 @@ Start it with [Foreman](https://github.com/ddollar/foreman)
or start each component separately
bundle exec rails s
bundle exec rake sidekiq:start
script/background_jobs start
### Run the tests
......
......@@ -3,6 +3,7 @@
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
ldap_groups_path: "/api/:version/ldap/groups.json"
namespaces_path: "/api/:version/namespaces.json"
# Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest
......@@ -50,6 +51,20 @@
).done (users) ->
callback(users)
# Return namespaces list. Filtered by query
namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (namespaces) ->
callback(namespaces)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
......
......@@ -123,6 +123,10 @@ $ ->
$(@).next('table').show()
$(@).remove()
$(".content").on "click", ".js-details-expand", ->
$(@).next('.js-details-contain').removeClass("hide")
$(@).remove()
(($) ->
_chosen = $.fn.chosen
$.fn.extend chosen: (options) ->
......
$ ->
namespaceFormatResult = (namespace) ->
markup = "<div class='namespace-result'>"
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
markup += "<span class='namespace-path'>" + namespace.path + "</span>"
markup += "</div>"
markup
formatSelection = (namespace) ->
namespace.kind + ": " + namespace.path
$('.ajax-namespace-select').each (i, select) ->
$(select).select2
placeholder: "Search for namespace"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.namespaces query.term, (namespaces) ->
data = { results: namespaces }
query.callback(data)
dropdownCssClass: "ajax-namespace-dropdown"
formatResult: namespaceFormatResult
formatSelection: formatSelection
......@@ -233,10 +233,12 @@ var NoteList = {
form.show();
var textarea = form.find("textarea");
var p = $("<p></p>").text(textarea.val());
var hidden_div = $('<div class="note-original-content"></div>').append(p);
form.append(hidden_div);
hidden_div.hide();
if (form.find(".note-original-content").length === 0) {
var p = $("<p></p>").text(textarea.val());
var hidden_div = $('<div class="note-original-content"></div>').append(p);
form.append(hidden_div);
hidden_div.hide();
}
textarea.focus();
},
......@@ -532,6 +534,8 @@ var NoteList = {
note_text.html(response.note).show();
var note_form = note_li.find(".note-edit-form");
var original_content = note_form.find(".note-original-content");
original_content.remove();
note_form.hide();
note_form.find(".btn-save").enableButton();
......
......@@ -6,10 +6,10 @@ class Project
@initEvents()
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
$('#project_issues_enabled').change ->
if ($(this).is(':checked') == true)
$('#project_issues_tracker').removeAttr('disabled')
......@@ -29,7 +29,7 @@ class Project
$ ->
# Git clone panel switcher
scope = $ '.project_clone_holder'
scope = $ '.git-clone-holder'
if scope.length > 0
$('a, button', scope).click ->
$('a, button', scope).removeClass 'active'
......@@ -40,3 +40,9 @@ $ ->
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
$('.hide-no-ssh-message').on 'click', (e) ->
path = '/'
$.cookie('hide_no_ssh_message', 'false', { path: path })
$(@).parents('.no-ssh-key-message').hide()
e.preventDefault()
......@@ -86,13 +86,6 @@ span.update-author {
font-weight: bold;
}
.label {
padding: 1px 4px;
font-size: 12px;
font-style: normal;
font-weight: normal;
}
.field_with_errors {
display: inline;
}
......@@ -136,6 +129,10 @@ p.time {
}
}
.highlight {
text-shadow: none;
}
.highlight_word {
border-bottom: 2px solid #F90;
}
......@@ -223,7 +220,6 @@ li.note {
.error-message {
padding: 10px;
background: #C67;
padding-left: 20px;
margin: 0;
color: #FFF;
......@@ -231,8 +227,18 @@ li.note {
color: #fff;
text-decoration: underline;
}
&.centered {
text-align: center;
}
.no-ssh-key-message {
padding: 10px 0;
background: #C67;
margin: 0;
color: #FFF;
text-align: center;
a {
color: #fff;
text-decoration: underline;
}
}
......@@ -349,3 +355,40 @@ table {
@extend .btn-new;
padding: 5px 15px;
}
.broadcast-message {
padding: 10px;
text-align: center;
background: #555;
color: #BBB;
}
.ajax-users-select {
width: 400px;
&.input-large {
width: 210px;
}
}
.user-result {
.user-image {
float: left;
}
.user-name {
}
.user-username {
color: #999;
}
}
.namespace-result {
.namespace-kind {
color: #AAA;
font-weight: normal;
}
.namespace-path {
margin-left: 10px;
font-weight: bolder;
}
}
......@@ -2,8 +2,8 @@
float: left;
margin-right: 12px;
width: 40px;
border: 1px solid #ddd;
padding: 1px;
@include border-radius(4px);
&.avatar-inline {
float: none;
......
......@@ -32,12 +32,9 @@
}
&.ui-box-show {
text-shadow: 0 1px 1px #fff;
color: #666;
margin:20px 0;
background: #FFF;
box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1;
@include linear-gradient(#fafafa, #f1f1f1);
background: #FAFAFA;
.control-group {
margin-bottom: 0;
......@@ -45,11 +42,13 @@
}
&.ui-box-danger {
background: #f7f7f7;
border: none;
.title {
@include linear-gradient(#F26E5E, #bd362f);
background: #D65;
color: #fff;
text-shadow: 0 1px 1px #900;
font-weight: bold;
}
}
......@@ -99,9 +98,9 @@
}
.title {
@include bg-gray-gradient;
border-bottom: 1px solid #CCC;
color: #456;
background-color: #EEE;
border-bottom: 1px solid #DDD;
color: #666;
font-size: 16px;
text-shadow: 0 1px 1px #fff;
padding: 0 10px;
......
.btn {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 13px;
line-height: $baseLineHeight;
font-weight: normal;
text-align: center;
vertical-align: middle;
cursor: pointer;
border: 1px solid #BBB;
color: $style_color;
@include border-radius($baseBorderRadius);
@include box-shadow(inset 0 1px 0 rgba(255,255,255,.2));
@include linear-gradient(#f1f1f1, #e1e1e1);
text-shadow: 0 1px 1px #FFF;
text-decoration: none;
background-image: none;
border: 1px solid transparent;
white-space: nowrap;
padding: 6px 12px;
font-size: 13px;
line-height: 18px;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
color: #444444;
background-color: #fff;
border-color: #ccc;
text-shadow: none;
&.hover,
&:hover {
color: $style_color;
background: #f1f1f1;
border-color: #AAA;
color: #444444;
text-decoration: none;
@include linear-gradient(#fAfAfA, #f1f1f1);
background-color: #ebebeb;
border-color: #adadad;
}
&.focus,
&:focus {
color: #444444;
text-decoration: none;
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
&.active,
&:active {
background-image: none;
outline: 0;
text-decoration: none;
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
background-image: none;
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
&.disabled,
&[disabled] {
cursor: default;
background-image: none;
@include opacity(65);
@include box-shadow(none);
cursor: not-allowed;
pointer-events: none;
opacity: 0.65;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
}
&.btn-primary {
color: #FFF;
border-color: #189;
text-shadow: 0 1px 1px #189;
@include linear-gradient(#4AC, #289);
color: #ffffff;
background-color: #429bca;
border-color: #358ebd;
&.hover,
&:hover,
&.disabled,
&[disabled] {
color: #FFF;
background: #389;
color: #ffffff;
background-color: #3286b1;
border-color: #286e8e;
}
}
&.btn-success {
color: #FFF;
border-color: #1A1;
text-shadow: 0 1px 1px #FFF;
text-shadow: 0 1px 1px #181;
@include linear-gradient(#62C452, #51a351);
color: #ffffff;
background-color: #5cb85c;
border-color: #4cae4c;
&.hover,
&:hover,
&.disabled,
&[disabled] {
color: #FFF;
background: #2A2;
color: #ffffff;
background-color: #47a447;
border-color: #398439;
}
}
&.btn-danger {
color: #FFF;
text-shadow: 0 1px 1px #811;
border-color: #BD362F;
@include linear-gradient(#EE5F5B, #BD362F);
color: #ffffff;
background-color: #d9534f;
border-color: #d43f3a;
&.hover,
&:hover,
&.disabled,
&[disabled] {
color: #FFF;
background: #A22;
color: #ffffff;
background-color: #d2322d;
border-color: #ac2925;
}
}
......@@ -138,4 +148,11 @@
margin-right: 7px;
float: left;
}
&.btn-block {
width: 100%;
margin: 0;
padding: 6px 0;
margin-bottom: 15px;
}
}
/** COLORS **/
.cgray { color: gray }
.clgray { color: #BBB }
.cred { color: #D12F19 }
.cgreen { color: #4a2 }
.cblue { color: #29A }
......@@ -88,6 +89,19 @@ pre.well-pre {
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
}
.label {
padding: 2px 4px;
font-size: 12px;
font-style: normal;
font-weight: normal;
&.label-gray {
background-color: #eee;
color: #999;
text-shadow: none;
}
}
/** Big Labels **/
.state-label {
font-size: 14px;
......@@ -109,3 +123,7 @@ pre.well-pre {
color: #FFF;
}
}
.dropdown-menu > li > a {
text-shadow: none;
}
......@@ -11,8 +11,8 @@
}
.file-title {
border-bottom: 1px solid #bbb;
@include bg-dark-gray-gradient;
background: #DDD;
border-bottom: 1px solid #CCC;
text-shadow: 0 1px 1px #fff;
margin: 0;
font-weight: normal;
......
......@@ -3,6 +3,16 @@ form {
label {
@extend .control-label;
&.radio-label {
text-align: left;
width: 100%;
margin-left: 0;
input[type="radio"] {
margin-top: 1px !important;
}
}
}
}
......@@ -49,3 +59,9 @@ fieldset legend {
font-size: 16px;
margin-bottom: 10px;
}
.datetime-controls {
select {
width: 100px;
}
}
......@@ -25,6 +25,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));
background-image: -webkit-linear-gradient($from, $to);
background-image: -moz-linear-gradient($from, $to);
background-image: -ms-linear-gradient($from, $to);
background-image: -o-linear-gradient($from, $to);
}
......@@ -45,6 +46,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -ms-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
}
......@@ -53,6 +55,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
......@@ -60,6 +63,7 @@
background: #eee;
background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -ms-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
}
......@@ -85,10 +89,26 @@
}
code { padding: 0 4px; }
h1 { margin-top: 30px;}
h2 { margin-top: 25px;}
h3 { margin-top: 20px;}
h4 { margin-top: 15px;}
h1 {
margin-top: 45px;
font-size: 2.5em;
}
h2 {
margin-top: 40px;
font-size: 2em;
}
h3 {
margin-top: 35px;
font-size: 2em;
}
h4 {
margin-top: 30px;
font-size: 1.5em;
}
blockquote p {
color: #888;
......@@ -103,6 +123,16 @@
background: #EEE;
}
}
code {
font-size: inherit;
font-weight: inherit;
color: #555;
}
li {
line-height: 1.5;
}
}
@mixin page-title {
......
......@@ -21,3 +21,9 @@
.controls { margin-left: 130px; }
.form-actions { padding-left: 130px; background: #fff }
}
.broadcast-messages {
.message {
line-height: 2;
}
}
......@@ -16,36 +16,29 @@
.header {
@extend .clearfix;
background: #DDD;
border-bottom: 1px solid #CCC;
padding: 5px 5px 5px 10px;
color: #555;
border-bottom: 1px solid #CCC;
background: #eee;
// TODO Replace with linear-gradient mixin
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
a{
color: $style_color;
}
> span {
font-family: $monospace_font;
font-size: 14px;
line-height: 30px;
line-height: 2;
}
a.view-file{
.view-file {
font-weight: bold;
float: right;
background-color: #EEE;
}
.commit-short-id{
.commit-short-id {
font-family: $monospace_font;
font-size: smaller;
}
.file-mode{
.file-mode {
font-family: $monospace_font;
}
}
......@@ -55,13 +48,13 @@
background: #FFF;
color: #333;
font-size: 12px;
.old{
span.idiff{
.old {
span.idiff {
background-color: #FAA;
}
}
.new{
span.idiff{
.new {
span.idiff {
background-color: #AFA;
}
}
......@@ -293,6 +286,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
ul, li{
......
......@@ -51,7 +51,7 @@
li {
&.active {
a {
@include linear-gradient(#f5f5f5, #eee);
background-color: #EEE;
border-bottom: 1px solid #EEE !important;
&:hover {
background: #eee;
......@@ -74,7 +74,7 @@
}
.project-name, .group-name {
font-size: 16px;
font-size: 15px;
}
.arrow {
......
......@@ -47,7 +47,7 @@
.event-title {
color: #333;
font-weight: normal;
font-size: 15px;
font-size: 14px;
.author_name {
color: #333;
}
......@@ -60,14 +60,14 @@
color: #666;
}
.event-note {
color: #555;
color: #666;
margin-top: 5px;
pre {
border: none;
background: #f9f9f9;
border-radius: 0;
color: #555;
color: #666;
margin: 0 20px;
}
......@@ -142,19 +142,19 @@
.filter_icon {
a {
text-align:center;
border-left: 3px solid $primary_color;
background: #f9f9f9;
background: #EEE;
margin-bottom: 10px;
float: left;
padding: 9px 7px;
padding: 9px 6px;
font-size: 18px;
width: 26px;
@include border-radius(3px);
}
&.inactive {
a {
color: #DDD;
border-left: 3px solid #EEE;
background: #f9f9f9;
}
}
}
......
......@@ -36,8 +36,8 @@ header {
float: left;
margin-right: 9px;
position: relative;
top: -5px;
padding-top: 5px;
top: -3px;
padding-top: 3px;
a {
float: left;
......
......@@ -110,9 +110,29 @@
.merge-request-angle {
text-align: center;
margin: 0;
margin: 0 auto;
background: #eee;
border-radius: 100px;
width: 60px;
line-height: 60px;
color: #777;
text-shadow: 0 1px 2px #FFF;
}
.merge-request-form-info {
padding: 15px 0;
padding-top: 15px;
}
.merge-request-branches {
.commit-row-message {
font-weight: normal !important;
}
.chosen-container .chosen-single {
padding: 2px 0 2px 10px;
span {
font-weight: bold;
color: #555;
}
}
}
......@@ -14,25 +14,76 @@
}
}
.project_clone_panel {
.project-home-panel {
border-bottom: 1px solid #DDD;
padding-bottom: 25px;
margin-bottom: 30px;
.project-home-title {
font-size: 18px;
color: #777;
margin: 0;
line-height: 32px;
}
.project-home-dropdown {
margin-left: 10px;
float: right;
}
.project-home-extra {
margin-top: 15px;
.project-home-desc {
float: left;
color: #999;
}
.project-home-links {
float: right;
a {
margin-left: 10px;
}
}
}
.public-label {
font-size: 14px;
background: #f1f1f1;
padding: 8px 10px;
border-radius: 4px;
margin-left: 10px;
color: #888;
text-shadow: 0 1px 1px #FFF;
}
}
.git-clone-holder {
float: right;
border: 1px solid #E1E1E1;
@include border-radius(4px);
@include bg-gray-gradient;
padding: 4px 7px;
border: 1px solid #CCC;
margin-bottom: 20px;
.btn {
padding: 4px 12px;
margin-left: 3px;
border: none;
background: none;
box-shadow: none;
color: #29b;
padding: 6px;
&.active {
color: #333;
font-weight: bold;
}
}
}
.project_clone_holder {
input[type="text"] {
margin-left: 2px;
border: none;
border-radius: 0;
border-left: 1px solid #E1E1E1;
@extend .monospace;
border: 1px solid #BBB;
box-shadow: none;
margin-left: -1px;
background: #FFF;
background: #FAFAFA;
padding: 6px 10px;
}
}
......@@ -81,16 +132,16 @@ ul.nav.nav-projects-tabs {
.my-projects {
li {
.project-title {
font-size: 14px;
}
.project-info {
margin-bottom: 10px;
}
.access-icon i {
.access-icon {
color: #AAA;
margin-left: 10px;
i {
color: #AAA;
}
}
}
}
......@@ -115,3 +166,61 @@ ul.nav.nav-projects-tabs {
color: #777;
}
}
.project-side {
.btn-block {
background-image: none;
background-color: #F1f1f1;
border-color: #EEE;
&:hover {
background-color: #eee;
border-color: #DDD;
}
}
.project-fork-icon {
float: left;
font-size: 26px;
margin-right: 10px;
line-height: 1.5;
}
}
.transfer-project .chosen-container {
min-width: 200px;
}
/** Branch/tag selector **/
.project-refs-form {
margin: 0;
span {
background:none !important;
position:static !important;
width:auto !important;
height:auto !important;
}
}
.project-refs-select {
width: 120px;
}
.project-refs-form .chosen-container {
position: relative;
top: 0;
left: 0;
margin-right: 10px;
.chosen-single span {
font-weight: bold;
color: #555;
}
&.chosen-container-active {
.chosen-drop {
min-width: 400px;
}
.chosen-results {
max-height: 400px;
}
}
}
/* CHZN reset few styles */
.chosen-container-single .chosen-single {
background: #FFF;
border: 1px solid #bbb;
box-shadow: none;
}
.chosen-container-active .chosen-single {
background: #fff;
}
.ajax-users-select {
width: 400px;
&.input-large {
width: 210px;
}
}
.user-result {
.user-image {
float: left;
}
.user-name {
}
.user-username {
color: #999;
}
}
/** Branch/tag selector **/
.project-refs-form {
margin: 0;
span {
background:none !important;
position:static !important;
width:auto !important;
height:auto !important;
}
}
.project-refs-select {
width: 120px;
}
.project-refs-form .chosen-container {
position: relative;
top: 0;
left: 0;
margin-right: 10px;
.chosen-drop {
min-width: 400px;
.chosen-results {
max-height: 300px;
}
.chosen-search input {
min-width: 365px;
}
}
}
/** Fix for Search Dropdown Border **/
/** Chosen.js selectbox style override **/
.chosen-container {
min-width: 100px;
.chosen-search {
input:focus {
@include box-shadow(none);
}
.chosen-single {
background: #EEE !important;
border: 1px solid #DDD !important;
@include box-shadow(none !important);
@include border-radius(4px !important);
}
.chosen-drop {
margin: 7px 0;
min-width: 200px;
border: 1px solid #bbb;
@include border-radius(0);
.chosen-results {
margin-top: 5px;
max-height: 300px;
.group-result {
color: $style_color;
border-bottom: 1px solid #EEE;
padding: 8px;
}
.active-result {
@include border-radius(0);
&.highlighted {
background: $hover;
color: $style_color;
}
&.result-selected {
background: #EEE;
border-left: 4px solid #CCC;
}
}
}
.chosen-search {
@include bg-gray-gradient;
input {
min-width: 165px;
border-color: #CCC;
}
}
.chosen-results li.highlighted {
background: #29b;
}
}
.chosen-container .chosen-single,
.chosen-container.chosen-with-drop .chosen-single {
@include bg-light-gray-gradient;
div {
background: transparent;
border-left: none;
.chosen-drop {
margin-top: 10px;
border: 1px solid #DDD !important;
@include border-radius(4px !important);
}
span {
font-weight: normal;
.chosen-search input {
border: 1px solid #CCC !important;
@include box-shadow(none !important);
}
}
/** Select2 styling **/
.select2-container .select2-choice {
background: #f1f1f1;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, whitesmoke), to(#e1e1e1));
background-image: -webkit-linear-gradient(whitesmoke 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(whitesmoke 6.6%, #e1e1e1);
background-image: -o-linear-gradient(whitesmoke 6.6%, #e1e1e1);
@include bg-light-gray-gradient;
}
.select2-container .select2-choice div {
......
......@@ -27,6 +27,12 @@
background: #435;
border-left: 1px solid #658;
}
.nav > li > a {
color: #98B;
}
.search-input {
border-color: #98B;
}
}
}
}
......
......@@ -23,12 +23,17 @@
background-color: #373D47;
}
}
.separator {
background: #373D47;
border-left: 1px solid #575D67;
}
.nav > li > a {
color: #979DA7;
}
.search-input {
border-color: #979DA7;
}
}
}
.separator {
background: #31363E;
border-left: 1px solid #666;
}
}
}
......@@ -27,6 +27,12 @@
background: #234;
border-left: 1px solid #456;
}
.nav > li > a {
color: #89A;
}
.search-input {
border-color: #89A;
}
}
}
}
......
......@@ -17,4 +17,3 @@ class BaseContext
abilities.allowed?(object, action, subject)
end
end
......@@ -18,6 +18,7 @@ class CommitLoadContext < BaseContext
result[:note] = project.build_commit_note(commit)
result[:line_notes] = line_notes
result[:notes_count] = project.notes.for_commit_id(commit.id).count
result[:branches] = project.repository.branch_names_contains(commit.id)
begin
result[:suppress_diff] = true if commit.diff_suppress? && !params[:force_show_diff]
......
module Files
class BaseContext < ::BaseContext
attr_reader :ref, :path
def initialize(project, user, params, ref, path = nil)
@project, @current_user, @params = project, user, params.dup
@ref = ref
@path = path
end
private
def error(message)
{
error: message,
status: :error
}
end
def success
{
error: '',
status: :success
}
end
def repository
project.repository
end
end
end
module Files
class CreateContext < BaseContext
def execute
allowed = if project.protected_branch?(ref)
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
unless allowed
return error("You are not allowed to create file in this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
file_name = File.basename(path)
file_path = path
unless file_name =~ Gitlab::Regex.path_regex
return error("Your changes could not be commited, because file name contains not allowed characters")
end
blob = repository.blob_at(ref, file_path)
if blob
return error("Your changes could not be commited, because file with such name exists")
end
new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
created_successfully = new_file_action.commit!(
params[:content],
params[:commit_message]
)
if created_successfully
success
else
error("Your changes could not be commited, because the file has been changed")
end
end
end
end
module Files
class DeleteContext < BaseContext
def execute
allowed = if project.protected_branch?(ref)
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at(ref, path)
unless blob
return error("You can only edit text files")
end
delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
deleted_successfully = delete_file_action.commit!(
nil,
params[:commit_message]
)
if deleted_successfully
success
else
error("Your changes could not be commited, because the file has been changed")
end
end
end
end
module Files
class UpdateContext < BaseContext
def execute
allowed = if project.protected_branch?(ref)
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at(ref, path)
unless blob
return error("You can only edit text files")
end
new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
created_successfully = new_file_action.commit!(
params[:content],
params[:commit_message]
)
if created_successfully
success
else
error("Your changes could not be commited, because the file has been changed")
end
end
end
end
......@@ -47,8 +47,6 @@ module Projects
@project.creator = current_user
if @project.save
@project.discover_default_branch
unless @project.group
@project.users_projects.create(
project_access: UsersProject::MASTER,
......
......@@ -3,6 +3,16 @@ module Projects
def execute(role = :default)
params[:project].delete(:namespace_id)
params[:project].delete(:public) unless can?(current_user, :change_public_mode, project)
new_branch = params[:project].delete(:default_branch)
if project.repository.exists? && new_branch != project.repository.root_ref
GitlabShellWorker.perform_async(
:update_repository_head,
project.path_with_namespace,
new_branch
)
end
project.update_attributes(params[:project], as: role)
end
end
......
......@@ -10,11 +10,14 @@ class SearchContext
query = Shellwords.shellescape(query) if query.present?
return result unless query.present?
projects = Project.where(id: project_ids)
result[:projects] = projects.search(query).limit(20)
result[:projects] = Project.where("projects.id in (?) OR projects.public = true", project_ids).search(query).limit(20)
# Search inside single project
single_project_search(Project.where(id: project_ids), query)
result
end
def single_project_search(projects, query)
project = projects.first if projects.length == 1
if params[:search_code].present?
......@@ -24,7 +27,6 @@ class SearchContext
result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20)
result[:wiki_pages] = []
end
result
end
def result
......
class Admin::BackgroundJobsController < Admin::ApplicationController
def show
@sidekiq_processes = `ps -U #{Settings.gitlab.user} -o euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep`
end
end
class Admin::BroadcastMessagesController < Admin::ApplicationController
before_filter :broadcast_messages
def index
@broadcast_message = BroadcastMessage.new
end
def create
@broadcast_message = BroadcastMessage.new(params[:broadcast_message])
if @broadcast_message.save
redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully created.'
else
render :index
end
end
def destroy
BroadcastMessage.find(params[:id]).destroy
respond_to do |format|
format.html { redirect_to :back }
format.js { render nothing: true }
end
end
protected
def broadcast_messages
@broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page])
end
end
......@@ -2,5 +2,6 @@ class Admin::DashboardController < Admin::ApplicationController
def index
@projects = Project.order("created_at DESC").limit(10)
@users = User.order("created_at DESC").limit(10)
@groups = Group.order("created_at DESC").limit(10)
end
end
class Admin::ProjectsController < Admin::ApplicationController
before_filter :project, only: [:edit, :show, :update, :destroy, :team_update]
before_filter :project, only: [:show, :transfer]
before_filter :group, only: [:show, :transfer]
before_filter :repository, only: [:show, :transfer]
def index
owner_id = params[:owner_id]
......@@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController
end
def show
@repository = @project.repository
@group = @project.group
end
def transfer
result = ::Projects::TransferContext.new(@project, current_user, project: params).execute(:admin)
if result
redirect_to [:admin, @project]
else
render :show
end
end
protected
......@@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController
@project = Project.find_with_namespace(id)
@project || render_404
end
def group
@group ||= project.group
end
def repository
@repository ||= project.repository
end
end
......@@ -104,7 +104,7 @@ class GroupsController < ApplicationController
# Dont allow unauthorized access to group
def authorize_read_group!
unless projects.present? or can?(current_user, :read_group, @group)
unless @group and (projects.present? or can?(current_user, :read_group, @group))
return render_404
end
end
......
......@@ -23,4 +23,10 @@ class Projects::ApplicationController < ApplicationController
'public_projects'
end
end
def require_branch_head
unless @repository.branch_names.include?(@ref)
redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
end
end
end
class Projects::BaseTreeController < Projects::ApplicationController
include ExtractsPath
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
end
......@@ -7,9 +7,30 @@ class Projects::BlobController < Projects::ApplicationController
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :blob
def show
@blob = @repository.blob_at(@commit.id, @path)
end
def destroy
result = Files::DeleteContext.new(@project, current_user, params, @ref, @path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully commited"
redirect_to project_tree_path(@project, @ref)
else
flash[:alert] = result[:error]
render :show
end
end
private
def blob
@blob ||= @repository.blob_at(@commit.id, @path)
return not_found! unless @blob
not_found! unless @blob
@blob
end
end
......@@ -22,6 +22,7 @@ class Projects::CommitController < Projects::ApplicationController
@note = result[:note]
@line_notes = result[:line_notes]
@branches = result[:branches]
@notes_count = result[:notes_count]
@target_type = :commit
@target_id = @commit.id
......
# Controller for edit a repository's file
class Projects::EditTreeController < Projects::ApplicationController
include ExtractsPath
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :edit_requirements, only: [:show, :update]
class Projects::EditTreeController < Projects::BaseTreeController
before_filter :require_branch_head
before_filter :blob
def show
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end
def update
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
updated_successfully = edit_file_action.commit!(
params[:content],
params[:commit_message],
params[:last_commit]
)
result = Files::UpdateContext.new(@project, current_user, params, @ref, @path).execute
if updated_successfully
redirect_to project_blob_path(@project, @id), notice: "Your changes have been successfully commited"
if result[:status] == :success
flash[:notice] = "Your changes have been successfully commited"
redirect_to project_blob_path(@project, @id)
else
flash[:notice] = "Your changes could not be commited, because the file has been changed"
flash[:alert] = result[:error]
render :show
end
end
private
def edit_requirements
@blob = @repository.blob_at(@commit.id, @path)
unless @blob
redirect_to project_blob_path(@project, @id), notice: "You can only edit text files"
end
allowed = if project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
return access_denied! unless allowed
def blob
@blob ||= @repository.blob_at(@commit.id, @path)
end
end
class Projects::NewTreeController < Projects::BaseTreeController
before_filter :require_branch_head
def show
end
def update
file_path = File.join(@path, File.basename(params[:file_name]))
result = Files::CreateContext.new(@project, current_user, params, @ref, file_path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully commited"
redirect_to project_blob_path(@project, File.join(@ref, file_path))
else
flash[:alert] = result[:error]
render :show
end
end
end
class Projects::TeamMembersController < Projects::ApplicationController
# Authorize
before_filter :authorize_admin_project!
before_filter :authorize_admin_project!, except: :leave
layout "project_settings"
......@@ -45,6 +45,15 @@ class Projects::TeamMembersController < Projects::ApplicationController
end
end
def leave
project.users_projects.find_by_user_id(current_user).destroy
respond_to do |format|
format.html { redirect_to :back }
format.js { render nothing: true }
end
end
def apply_import
giver = Project.find(params[:source_project_id])
status = @project.team.import(giver)
......
# Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController
include ExtractsPath
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
class Projects::TreeController < Projects::BaseTreeController
def show
return not_found! if tree.entries.empty?
......
......@@ -62,10 +62,6 @@ class ProjectsController < ApplicationController
@events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0)
# Ensure project default branch is set if it possible
# Normally it defined on push or during creation
@project.discover_default_branch
respond_to do |format|
format.html do
if @project.empty_repo?
......
......@@ -84,8 +84,8 @@ module ApplicationHelper
repository = @project.repository
options = [
["Branch", repository.branch_names ],
[ "Tag", repository.tag_names ]
["Branches", repository.branch_names],
["Tags", repository.tag_names]
]
# If reference is commit id -
......@@ -126,6 +126,9 @@ module ApplicationHelper
# Skip if user already created appropriate MR
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
# Skip if user removed branch right after that
return false unless project.repository.branch_names.include?(event.branch_name)
true
end
......@@ -184,14 +187,6 @@ module ApplicationHelper
Gitlab.config.extra
end
def public_icon
content_tag :i, nil, class: 'icon-globe cblue'
end
def private_icon
content_tag :i, nil, class: 'icon-lock cgreen'
end
def search_placeholder
if @project && @project.persisted?
"Search in this project"
......@@ -208,4 +203,8 @@ module ApplicationHelper
line += "..." if lines.size > 1
line
end
def broadcast_message
BroadcastMessage.current
end
end
......@@ -94,6 +94,17 @@ module CommitsHelper
crumbs.html_safe
end
# Return Project default branch, if it present in array
# Else - first branch in array (mb last actual branch)
def commit_default_branch(project, branches)
branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop
end
# Returns the sorted alphabetically links to branches, separated by a comma
def commit_branches_links(project, branches)
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end
protected
# Private: Returns a link to a person. If the person has a matching user and
......@@ -114,7 +125,9 @@ module CommitsHelper
source_name
end
user = User.where('name like ? or email like ?', source_name, source_email).first
# Prefer email match over name match
user = User.where(email: source_email).first
user ||= User.where(name: source_name).first
options = {
class: "commit-#{options[:source]}-link has_tooltip",
......
......@@ -127,4 +127,10 @@ module EventsHelper
text = truncate(text, length: 150)
sanitize(markdown(text), tags: %w(a img b pre p))
end
def event_commit_title(message)
escape_once(truncate(message.split("\n").first, length: 70))
rescue
"--broken encoding"
end
end
......@@ -64,7 +64,9 @@ module GitlabMarkdownHelper
# ref - name of the branch or reference, eg. stable
# requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from
# wiki - whether the markdown is from wiki or not
def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false)
def create_relative_links(text, project, ref, requested_path, wiki = false)
@path_to_satellite = project.satellite.path
project_path_with_namespace = project.path_with_namespace
paths = extract_paths(text)
paths.each do |file_path|
new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref)
......@@ -145,13 +147,18 @@ module GitlabMarkdownHelper
def file_exists?(path)
return false if path.nil? || path.empty?
File.exists?(Rails.root.join(path))
File.exists?(path_on_fs(path))
end
# Check if the path is pointing to a directory(tree) or a file(blob)
# eg. doc/api is directory and doc/README.md is file
def local_path(path)
File.directory?(Rails.root.join(path)) ? "tree" : "blob"
File.directory?(path_on_fs(path)) ? "tree" : "blob"
end
# Path to the file in the satellites repository on the filesystem
def path_on_fs(path)
[@path_to_satellite, path].join("/")
end
# We will assume that if no ref exists we can point to master
......
......@@ -2,4 +2,23 @@ module GroupsHelper
def remove_user_from_group_message(group, user)
"You are going to remove #{user.name} from #{group.name} Group. Are you sure?"
end
def group_head_title
title = @group.name
title = if current_action?(:issues)
"Issues - " + title
elsif current_action?(:merge_requests)
"Merge requests - " + title
elsif current_action?(:members)
"Members - " + title
elsif current_action?(:edit)
"Settings - " + title
else
title
end
title
end
end
module IconsHelper
def boolean_to_icon(value)
if value.to_s == "true"
content_tag :i, nil, class: 'icon-ok cgreen'
else
content_tag :i, nil, class: 'icon-off clgray'
end
end
def public_icon
content_tag :i, nil, class: 'icon-globe cblue'
end
def private_icon
content_tag :i, nil, class: 'icon-lock cgreen'
end
end
module NamespacesHelper
def namespaces_options(selected = :current_user, scope = :default)
groups = current_user.owned_groups.select {|n| n.type == 'Group'}
users = current_user.namespaces.reject {|n| n.type == 'Group'}
groups = current_user.owned_groups
users = [current_user.namespace]
group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
......@@ -16,4 +16,13 @@ module NamespacesHelper
grouped_options_for_select(options, selected)
end
def namespace_select_tag(id, opts = {})
css_class = "ajax-namespace-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
hidden_field_tag(id, value, class: css_class)
end
end
......@@ -45,7 +45,10 @@ module ProjectsHelper
link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
end
else
project.name
owner = project.namespace.owner
content_tag :span do
link_to(simple_sanitize(owner.name), user_path(owner)) + " / " + project.name
end
end
end
......@@ -80,7 +83,7 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC").all
end
def project_issues_trackers
def project_issues_trackers(current_tracker = nil)
values = Project.issues_tracker.values.map do |tracker_key|
if tracker_key.to_sym == :gitlab
['GitLab', tracker_key]
......@@ -89,7 +92,7 @@ module ProjectsHelper
end
end
options_for_select(values)
options_for_select(values, current_tracker)
end
private
......@@ -140,4 +143,38 @@ module ProjectsHelper
# to calculate repo size - just show 'Unknown'
'unknown'
end
def project_head_title
title = @project.name_with_namespace
title = if current_controller?(:tree)
"#{@project.path}\/#{@path} at #{@ref} - " + title
elsif current_controller?(:issues)
if current_action?(:show)
"Issue ##{@issue.iid} - " + title
else
"Issues - " + title
end
elsif current_controller?(:blob)
"#{@project.path}\/#{@blob.path} at #{@ref} - " + title
elsif current_controller?(:commits)
"Commits at #{@ref} - " + title
elsif current_controller?(:merge_requests)
if current_action?(:show)
"Merge request ##{@merge_request.iid} - " + title
else
"Merge requests - " + title
end
elsif current_controller?(:wikis)
"Wiki - " + title
elsif current_controller?(:network)
"Network graph - " + title
elsif current_controller?(:graphs)
"Graphs - " + title
else
title
end
title
end
end
......@@ -5,7 +5,7 @@ module Emails
@group = @membership.group
mail(to: @membership.user.email,
subject: subject("access to group was granted"))
subject: subject("Access to group was granted"))
end
end
end
......@@ -3,14 +3,14 @@ module Emails
def new_issue_email(recipient_id, issue_id)
@issue = Issue.find(issue_id)
@project = @issue.project
mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.iid}", @issue.title))
mail(to: recipient(recipient_id), subject: subject("New issue ##{@issue.iid}", @issue.title))
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
@issue = Issue.find(issue_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
@project = @issue.project
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.iid}", @issue.title))
mail(to: recipient(recipient_id), subject: subject("Changed issue ##{@issue.iid}", @issue.title))
end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
......@@ -27,7 +27,7 @@ module Emails
@project = @issue.project
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
subject: subject("changed issue ##{@issue.iid}", @issue.title))
subject: subject("Changed issue ##{@issue.iid}", @issue.title))
end
end
end
......@@ -2,24 +2,24 @@ module Emails
module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.iid}", @merge_request.title))
mail(to: recipient(recipient_id), subject: subject("New merge request ##{@merge_request.iid}", @merge_request.title))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@merge_request = MergeRequest.find(merge_request_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.iid}", @merge_request.title))
mail(to: recipient(recipient_id), subject: subject("Changed merge request ##{@merge_request.iid}", @merge_request.title))
end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.iid}", @merge_request.title))
mail(to: recipient(recipient_id), subject: subject("Closed merge request ##{@merge_request.iid}", @merge_request.title))
end
def merged_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.iid}", @merge_request.title))
mail(to: recipient(recipient_id), subject: subject("Accepted merge request ##{@merge_request.iid}", @merge_request.title))
end
end
......
......@@ -4,27 +4,27 @@ module Emails
@note = Note.find(note_id)
@commit = @note.noteable
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
mail(to: recipient(recipient_id), subject: subject("Note for commit #{@commit.short_id}", @commit.title))
end
def note_issue_email(recipient_id, note_id)
@note = Note.find(note_id)
@issue = @note.noteable
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.iid}"))
mail(to: recipient(recipient_id), subject: subject("Note for issue ##{@issue.iid}"))
end
def note_merge_request_email(recipient_id, note_id)
@note = Note.find(note_id)
@merge_request = @note.noteable
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for merge request ##{@merge_request.iid}"))
mail(to: recipient(recipient_id), subject: subject("Note for merge request ##{@merge_request.iid}"))
end
def note_wall_email(recipient_id, note_id)
@note = Note.find(note_id)
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note on wall"))
mail(to: recipient(recipient_id), subject: subject("Note on wall"))
end
end
end
......@@ -4,14 +4,14 @@ module Emails
@users_project = UsersProject.find user_project_id
@project = @users_project.project
mail(to: @users_project.user.email,
subject: subject("access to project was granted"))
subject: subject("Access to project was granted"))
end
def project_was_moved_email(project_id, user_id)
@user = User.find user_id
@project = Project.find project_id
mail(to: @user.email,
subject: subject("project was moved"))
subject: subject("Project was moved"))
end
end
end
# == Schema Information
#
# Table name: broadcast_messages
#
# id :integer not null, primary key
# message :text default(""), not null
# starts_at :datetime
# ends_at :datetime
# alert_type :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class BroadcastMessage < ActiveRecord::Base
attr_accessible :alert_type, :ends_at, :message, :starts_at
validates :message, presence: true
validates :starts_at, presence: true
validates :ends_at, presence: true
def self.current
where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last
end
end
......@@ -168,7 +168,7 @@ class Event < ActiveRecord::Base
end
def valid_push?
data[:ref]
data[:ref] && ref_name.present?
rescue => ex
false
end
......
......@@ -11,6 +11,8 @@
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
#
require "flowdock-git-hook"
......
......@@ -25,7 +25,7 @@ class HipchatService < Service
end
def description
'Simple web-based real-time group chat'
'Private group chat and IM'
end
def to_param
......@@ -71,5 +71,4 @@ class HipchatService < Service
message
end
end
......@@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base
include Issuable
include InternalId
ActsAsTaggableOn.strict_case_match = true
belongs_to :project
validates :project, presence: true
......
......@@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base
def send_update_instructions
projects.each(&:send_move_instructions)
end
def kind
type == 'Group' ? 'group' : 'user'
end
end
......@@ -9,7 +9,6 @@
# created_at :datetime not null
# updated_at :datetime not null
# creator_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
......@@ -27,8 +26,10 @@
class Project < ActiveRecord::Base
include Gitlab::ShellAdapter
extend Enumerize
ActsAsTaggableOn.strict_case_match = true
attr_accessible :name, :path, :description, :default_branch, :issues_tracker, :label_list,
attr_accessible :name, :path, :description, :issues_tracker, :label_list,
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
:wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin]
......@@ -36,6 +37,8 @@ class Project < ActiveRecord::Base
acts_as_taggable_on :labels, :issues_default_labels
attr_accessor :new_default_branch
# Relations
belongs_to :creator, foreign_key: "creator_id", class_name: "User"
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
......@@ -125,7 +128,7 @@ class Project < ActiveRecord::Base
end
def search query
where("projects.name LIKE :query OR projects.path LIKE :query", query: "%#{query}%")
joins(:namespace).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%")
end
def find_with_namespace(id)
......@@ -146,7 +149,7 @@ class Project < ActiveRecord::Base
end
def repository
@repository ||= Repository.new(path_with_namespace, default_branch)
@repository ||= Repository.new(path_with_namespace)
end
def saved?
......@@ -303,14 +306,6 @@ class Project < ActiveRecord::Base
end
end
def discover_default_branch
# Discover the default branch, but only if it hasn't already been set to
# something else
if repository.exists? && default_branch.nil?
update_attributes(default_branch: self.repository.discover_default_branch)
end
end
def update_merge_requests(oldrev, newrev, ref, user)
return true unless ref =~ /heads/
branch_name = ref.gsub("refs/heads/", "")
......@@ -320,7 +315,7 @@ class Project < ActiveRecord::Base
mrs = self.merge_requests.opened.by_branch(branch_name).all
# Update code for merge requests between project and project fork
mrs += self.fork_merge_requests.opened.by_branch(branch_name).all
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests
......@@ -450,4 +445,12 @@ class Project < ActiveRecord::Base
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end
def project_member(user)
users_projects.where(user_id: user).first
end
def default_branch
@default_branch ||= repository.root_ref if repository.exists?
end
end
......@@ -3,7 +3,7 @@ class Repository
attr_accessor :raw_repository, :path_with_namespace
def initialize(path_with_namespace, default_branch)
def initialize(path_with_namespace, default_branch = nil)
@path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
rescue Gitlab::Git::Repository::NoRepository
......@@ -57,7 +57,7 @@ class Repository
def recent_branches(limit = 20)
branches.sort do |a, b|
a.commit.committed_date <=> b.commit.committed_date
b.commit.committed_date <=> a.commit.committed_date
end[0..limit]
end
......
......@@ -36,6 +36,11 @@
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
# avatar :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
#
require 'carrierwave/orm/activerecord'
......@@ -69,20 +74,20 @@ class User < ActiveRecord::Base
# Namespace for personal projects
has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
# Namespaces (owned groups and own namespace)
has_many :namespaces, foreign_key: :owner_id
# Profile
has_many :keys, dependent: :destroy
# Groups
has_many :own_groups, class_name: "Group", foreign_key: :owner_id
has_many :owned_groups, through: :users_groups, source: :group, conditions: { users_groups: { group_access: UsersGroup::OWNER } }
has_many :users_groups, dependent: :destroy
has_many :groups, through: :users_groups
has_many :owned_groups, through: :users_groups, source: :group, conditions: { users_groups: { group_access: UsersGroup::OWNER } }
# Projects
has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects
has_many :projects, through: :users_projects
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
has_many :users_projects, dependent: :destroy
has_many :issues, dependent: :destroy, foreign_key: :author_id
......@@ -93,11 +98,6 @@ class User < ActiveRecord::Base
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects
has_many :projects, through: :users_projects
has_many :own_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :owned_projects, through: :namespaces, source: :projects
#
# Validations
......@@ -247,7 +247,7 @@ class User < ActiveRecord::Base
# Groups user has access to
def authorized_groups
@authorized_groups ||= begin
group_ids = (groups.pluck(:id) + own_groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
Group.where(id: group_ids).order('namespaces.name ASC')
end
end
......@@ -256,7 +256,7 @@ class User < ActiveRecord::Base
# Projects user has access to
def authorized_projects
@authorized_projects ||= begin
project_ids = owned_projects.pluck(:id)
project_ids = personal_projects.pluck(:id)
project_ids += groups_projects.pluck(:id)
project_ids += projects.pluck(:id)
project_ids += groups.joins(:shared_projects).pluck(:project_id)
......@@ -265,6 +265,12 @@ class User < ActiveRecord::Base
end
end
def owned_projects
@owned_projects ||= begin
Project.where(namespace_id: owned_groups.pluck(:id).push(namespace.id)).joins(:namespace)
end
end
# Team membership in authorized projects
def tm_in_authorized_projects
UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
......@@ -337,7 +343,7 @@ class User < ActiveRecord::Base
end
def several_namespaces?
namespaces.many? || owned_groups.any?
owned_groups.any?
end
def namespace_id
......@@ -402,4 +408,9 @@ class User < ActiveRecord::Base
self
end
def can_leave_project?(project)
project.namespace != namespace &&
project.project_member(self)
end
end
......@@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver
def after_update(project)
project.send_move_instructions if project.namespace_id_changed?
project.rename_repo if project.path_changed?
GitlabShellWorker.perform_async(
:update_repository_head,
project.path_with_namespace,
project.default_branch
) if project.default_branch_changed?
end
def before_destroy(project)
......
......@@ -24,7 +24,6 @@ class GitPushService
create_push_event
project.ensure_satellite_exists
project.discover_default_branch
project.repository.expire_cache
if push_to_existing_branch?(ref, oldrev)
......
......@@ -18,6 +18,10 @@ class ProjectTransferService
raise TransferError.new("Project with same path in target namespace already exists")
end
# Remove old satellite
project.satellite.destroy
# Apply new namespace id
project.namespace = new_namespace
project.save!
......@@ -29,8 +33,8 @@ class ProjectTransferService
# Move wiki repo also if present
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
# create satellite repo
project.ensure_satellite_exists
# Create a new satellite (reload project from DB)
Project.find(project.id).ensure_satellite_exists
# clear project cached events
project.reset_events_cache
......
%h3.page-title Background Jobs
%br
%p.light GitLab use #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
%hr
.ui-box
.title Sidekiq running processes
.ui-box-body
- if @sidekiq_processes.empty?
%h4.cred
%i.icon-warning-sign
There are no running sidekiq processes. Please restart GitLab
- else
%table.table
%thead
%th USER
%th
%th PID
%th
%th CPU
%th
%th MEM
%th
%th STATE
%th
%th START
%th
%th COMMAND
%th
- @sidekiq_processes.split("\n").each do |process|
- next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
- data = process.gsub!(/\s+/m, '|').strip.split('|')
%tr
- 6.times do
%td= data.shift
%td
%td= data.join(" ")
.clearfix
%p
%i.icon-exclamation-sign
If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
%p
%i.icon-exclamation-sign
If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{Settings.gitlab.user} -f sidekiq) and restart GitLab.
.ui-box
%iframe{src: sidekiq_path, width: '100%', height: 900, style: "border: none"}
%h4 Sidekiq running processes
- sidekiq_processes = `ps -eo euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep`
- if sidekiq_processes.empty?
%b There are no running sidekiq processes
%b Please restart GitLab
- else
.ui-box
%table.zebra-striped
%thead
%th USER
%th
%th PID
%th
%th CPU
%th
%th MEM
%th
%th STATE
%th
%th START
%th
%th COMMAND
%th
- sidekiq_processes.split("\n").each do |process|
- next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
- data = process.gsub!(/\s+/m, '|').strip.split('|')
%tr
- 6.times do
%td= data.shift
%td
%td= data.join(" ")
%b If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
%br
%b If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u git -f sidekiq) and restart GitLab.
%h3.page-title
Broadcast Messages
%p.light
Broadcast messages are displayed for every user and can be used to notify users about scheduled maintenance, recent upgrades and more.
%hr
= form_for [:admin, @broadcast_message] do |f|
-if @broadcast_message.errors.any?
.alert.alert-error
- @broadcast_message.errors.full_messages.each do |msg|
%p= msg
.control-group
= f.label :message
.controls
= f.text_area :message, class: "input-xxlarge", rows: 2, required: true
.control-group
= f.label :starts_at
.controls.datetime-controls
= f.datetime_select :starts_at
.control-group
= f.label :ends_at
.controls.datetime-controls
= f.datetime_select :ends_at
.form-actions
= f.submit "Add broadcast message", class: "btn btn-create"
-if @broadcast_messages.any?
%ul.bordered-list.broadcast-messages
- @broadcast_messages.each do |broadcast_message|
%li
.pull-right
- if broadcast_message.starts_at
%strong
#{broadcast_message.starts_at.to_s(:short)}
\...
- if broadcast_message.ends_at
%strong
#{broadcast_message.ends_at.to_s(:short)}
&nbsp;
= link_to [:admin, broadcast_message], method: :delete, remote: true, class: 'remove-row btn btn-tiny' do
%i.icon-remove.cred
.message= broadcast_message.message
= paginate @broadcast_messages
......@@ -51,6 +51,19 @@
= time_ago_in_words user.created_at
ago
.span4
%h4 Latest groups
%hr
- @groups.each do |group|
%p
= link_to [:admin, group] do
= group.name
%span.light.pull-right
= time_ago_in_words group.created_at
ago
%br
.row
.span4
%h4 Stats
%hr
......@@ -82,3 +95,34 @@
Milestones
%span.light.pull-right
= Milestone.count
.span4
%h4
Features
%hr
%p
Sign up
%span.light.pull-right
= boolean_to_icon gitlab_config.signup_enabled
%p
LDAP
%span.light.pull-right
= boolean_to_icon Gitlab.config.ldap.enabled
%p
Gravatar
%span.light.pull-right
= boolean_to_icon Gitlab.config.gravatar.enabled
%p
OmniAuth
%span.light.pull-right
= boolean_to_icon Gitlab.config.omniauth.enabled
.span4
%h4 Components
%hr
%p
GitLab
%span.pull-right
= Gitlab::VERSION
%p
GitLab Shell
%span.pull-right
= Gitlab::Shell.new.version
......@@ -6,7 +6,7 @@
%span= @group.errors.full_messages.first
.control-group.group_name_holder
= f.label :name do
Group name is
Group name
.controls
= f.text_field :name, placeholder: "Example Group", class: "input-xxlarge"
......@@ -17,7 +17,7 @@
.control-group.group_name_holder
= f.label :path do
%span.cred Group path is
%span.cred Group path
.controls
= f.text_field :path, placeholder: "example-group", class: "input-xxlarge danger"
%ul.cred
......
......@@ -6,7 +6,7 @@
%span= @group.errors.full_messages.first
.control-group
= f.label :name do
Group name is
Group name
.controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
.control-group.group-description-holder
......
......@@ -25,7 +25,7 @@
= @group.description
%li
%span.light Created at:
%span.light Created on:
%strong
= @group.created_at.stamp("March 1, 1999")
......
......@@ -35,7 +35,7 @@
= @project.creator.try(:name) || '(deleted)'
%li
%span.light Created at:
%span.light Created on:
%strong
= @project.created_at.stamp("March 1, 1999")
......@@ -74,6 +74,23 @@
%span.cgreen
%i.icon-lock
Private
.ui-box
.title
Transfer project
.ui-box-body
= form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
.control-group
= f.label :namespace_id, "Namespace"
.controls
= namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
.control-group
.controls
= f.submit 'Transfer', class: 'btn btn-primary'
.span6
- if @group
.ui-box
......
......@@ -54,15 +54,20 @@
.span9
%ul.bordered-list.my-projects.top-list
- @projects.each do |project|
%li
%li.my-project-row
%h4.project-title
%span.access-icon
- if project.public
= public_icon
- else
= private_icon
= link_to project_path(project), class: dom_class(project) do
%strong= project.name_with_namespace
= project.name_with_namespace
- if project.public
%small.access-icon
= public_icon
Public
- if current_user.can_leave_project?(project)
.pull-right
= link_to leave_project_team_members_path(project), confirm: "Leave project?", method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do
%i.icon-signout
Leave
- if project.forked_from_project
%small.pull-right
......@@ -81,6 +86,7 @@
%span.light Last activity:
%span.date= project_last_activity(project)
- if @projects.blank?
%li
%h3.nothing_here_message There are no projects here.
......
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.submit "Resend confirmation instructions" %></div>
<% end %>
<%= render partial: "devise/shared/links" %>
.login-box
%h3.page-title Resend confirmation instructions
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
= devise_error_messages!
= f.email_field :email, placeholder: 'Email'
%div= f.submit "Resend confirmation instructions", class: 'btn btn-success'
%hr
= link_to "Sign in", new_session_path(resource_name)
......@@ -7,5 +7,8 @@
%div
= f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password"
%div
.clearfix.append-bottom-10
= f.submit "Change my password", class: "btn btn-primary"
.pull-right= render partial: "devise/shared/links"
= link_to "Sign in", new_session_path(resource_name), class: "btn pull-right"
%div
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
......@@ -2,4 +2,4 @@
.commit-row-title
= link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: ''
&nbsp;
= gfm escape_once(truncate(commit[:message], length: 70)) rescue "--broken encoding"
= gfm event_commit_title(commit[:message])
......@@ -28,7 +28,7 @@
%span= @group.errors.full_messages.first
.control-group
= f.label :name do
Group name is
Group name
.controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
......@@ -90,7 +90,7 @@
.title Remove group
.ui-box-body
%p
Remove of group will cause removing all child projects and resources.
Removing group will cause all child projects and resources to be removed.
%p
%strong Removed group can not be restored!
......
......@@ -4,7 +4,7 @@
%span= @group.errors.full_messages.first
.control-group
= f.label :name do
Group name is
Group name
.controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
......@@ -16,11 +16,11 @@
.control-group
.controls
%ul
%li Group is kind of directory for several projects
%li All created groups are private
%li People within a group see only projects they have access to
%li All projects of group will be stored in a group directory
%li You will be able to move existing projects into group
%li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group
.form-actions
= f.submit 'Create group', class: "btn btn-create"
......
%h2.page-title
GitLab
%span.light Enterprise Edition
.pull-right
.hero-unit
%h2
GitLab
%span.light Enterprise Edition
%span= Gitlab::VERSION
%small= Gitlab::REVISION
%p.slead
Self Hosted Git Management
%br
Fast, secure and stable solution based on Ruby on Rails.
%p.slead
GitLab is open source software to collaborate on code.
%br
Create projects and repositories, manage access and do code reviews.
%br
Read more about GitLab at #{link_to "gitlab.org", "http://gitlab.org/", target: "_blank"}.
.row
.span4
......
- if broadcast_message.present?
.broadcast-message
%i.icon-bullhorn
= broadcast_message.message
......@@ -2,6 +2,7 @@
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application", :'data-page' => body_data_page }
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard"
= render "layouts/flash"
%nav.main-nav
......
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "#{@group.name}"
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/head_panel", title: "group: #{@group.name}"
= render "layouts/flash"
......
......@@ -10,6 +10,8 @@
= link_to "Users", admin_users_path
= nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path
= nav_link(controller: :broadcast_messages) do
= link_to "Messages", admin_broadcast_messages_path
= nav_link(controller: :hooks) do
= link_to "Hooks", admin_hooks_path
= nav_link(controller: :background_jobs) do
......
......@@ -4,7 +4,7 @@
%i.icon-home
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree)) do
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
- if project_nav_tab? :commits
......
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
= render "layouts/head", title: project_head_title
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
= render "layouts/flash"
......
%p
%strong #{@note.author_name}
left next message:
wrote:
%cite{style: 'color: #666'}
= markdown(@note.note)
%p
= "New Merge Request !#{@merge_request.iid}"
= "New Merge Request ##{@merge_request.iid}"
%p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
......
New Merge Request <%= @merge_request.iid %>
New Merge Request #<%= @merge_request.iid %>
<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
......
%p
= "Reassigned Merge Request !#{@merge_request.iid}"
= "Reassigned Merge Request ##{@merge_request.iid}"
= link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
Assignee changed
......
Reassigned Merge Request <%= @merge_request.iid %>
Reassigned Merge Request #<%= @merge_request.iid %>
<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
......
......@@ -45,7 +45,7 @@
Username
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
%p
Changing your username will change path to all personl projects!
Changing your username will change path to all personal projects!
%div
= f.text_field :username, required: true, class: 'input-xlarge input-xpadding'
&nbsp;
......@@ -68,6 +68,6 @@
%li #{pluralize rp, 'personal project'} will be removed and cannot be restored
- if current_user.solo_owned_groups.present?
%li
Next groups will be abandoned. You should transfer or remove them:
The following groups will be abandoned. You should transfer or remove them:
%strong #{current_user.solo_owned_groups.map(&:name).join(', ')}
= link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove"
......@@ -8,7 +8,7 @@
%span.light Title:
%strong= @key.title
%li
%span.light Created at:
%span.light Created on:
%strong= @key.created_at.stamp("Aug 21, 2011")
.span8
......
......@@ -63,7 +63,7 @@
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-user-avatar-input hide"
%span.help-block The maximum file size allowed is 200KB.
%span.help-block The maximum file size allowed is 100KB.
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
.project_clone_panel
.row
.span8
.form-horizontal= render "shared/clone_panel"
.span3.pull-right
.pull-right
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
- if current_user.already_forked?(@project)
= link_to project_path(current_user.fork_of(@project)), class: 'btn grouped disabled' do
%i.icon-code-fork
Forked
- else
= link_to fork_project_path(@project), title: "Fork", class: "btn grouped", method: "POST" do
%i.icon-code-fork
Fork
- if can? current_user, :download_code, @project
= link_to archive_project_repository_path(@project), class: "btn grouped" do
%i.icon-download-alt
%span.only-wide Download
- if current_user
.dropdown.pull-right
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.icon-plus-sign-alt
%span.only-wide New
%b.caret
%ul.dropdown-menu
- if @project.issues_enabled && can?(current_user, :write_issue, @project)
%li
= link_to url_for_new_issue, title: "New Issue" do
Issue
- if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
%li
= link_to new_project_merge_request_path(@project), title: "New Merge Request" do
Merge Request
- if @project.snippets_enabled && can?(current_user, :write_snippet, @project)
%li
= link_to new_project_snippet_path(@project), title: "New Snippet" do
Snippet
- if can? current_user, :push_code, @project
%li.divider
%li
= link_to new_project_branch_path(@project) do
%i.icon-code-fork
Git branch
%li
= link_to new_project_tag_path(@project) do
%i.icon-tag
Git tag
- if can?(current_user, :admin_team_member, @project)
%li.divider
%li
= link_to new_project_team_member_path(@project), title: "New project member" do
Project member
- if current_user
.dropdown.pull-right
%a.dropdown-toggle.btn.btn-new{href: '#', "data-toggle" => "dropdown"}
%i.icon-reorder
%ul.dropdown-menu
- if @project.issues_enabled && can?(current_user, :write_issue, @project)
%li
= link_to url_for_new_issue, title: "New Issue" do
New issue
- if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
%li
= link_to new_project_merge_request_path(@project), title: "New Merge Request" do
New merge request
- if @project.snippets_enabled && can?(current_user, :write_snippet, @project)
%li
= link_to new_project_snippet_path(@project), title: "New Snippet" do
New snippet
- if can?(current_user, :admin_team_member, @project)
%li
= link_to new_project_team_member_path(@project), title: "New project member" do
New project member
- if can? current_user, :push_code, @project
%li.divider
%li
= link_to new_project_branch_path(@project) do
%i.icon-code-fork
Git branch
%li
= link_to new_project_tag_path(@project) do
%i.icon-tag
Git tag
.btn-group.tree-btn-group
-# only show edit link for text files
- if @blob.text?
= link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small", disabled: !allowed_tree_edit?
- if allowed_tree_edit?
= link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small"
- else
%span.btn.btn-small.disabled edit
= link_to "raw", project_raw_path(@project, @id), class: "btn btn-small", target: "_blank"
-# only show normal/blame view links for text files
- if @blob.text?
......@@ -10,3 +13,7 @@
- else
= link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty?
= link_to "history", project_commits_path(@project, @id), class: "btn btn-small"
- if allowed_tree_edit?
= link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do
remove
%div#modal-remove-blob.modal.hide
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title Remove #{@blob.name}
%p.light
From branch
%strong= @ref
.modal-body
= form_tag project_blob_path(@project, @id), method: :delete do
.control-group.commit_message-group
= label_tag 'commit_message', class: "control-label" do
Commit message
.controls
= text_area_tag 'commit_message', params[:commit_message], placeholder: "Removed this file because...", required: true, rows: 3
.control-group
.controls
= submit_tag 'Remove file', class: 'btn btn-remove'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
......@@ -2,3 +2,6 @@
= render 'shared/ref_switcher', destination: 'blob', path: @path
%div#tree-holder.tree-holder
= render 'blob', blob: @blob
- if allowed_tree_edit?
= render 'projects/blob/remove'
%ul.nav.nav-pills.nav-stacked
= nav_link(path: 'branches#recent') do
= link_to 'Recent', recent_project_branches_path(@project)
= link_to recent_project_branches_path(@project) do
Recent
.pull-right
= @repository.recent_branches.count
= nav_link(path: 'protected_branches#index') do
= link_to project_protected_branches_path(@project) do
Protected
%i.icon-lock
.pull-right
= @project.protected_branches.count
= nav_link(path: 'branches#index') do
= link_to 'All branches', project_branches_path(@project)
= link_to project_branches_path(@project) do
All branches
.pull-right
= @repository.branch_names.count
%hr
......
......@@ -39,6 +39,19 @@
- @commit.parents.each do |parent|
= link_to parent.id[0...10], project_commit_path(@project, parent)
- if @branches.any?
.commit-info-row
%span.cgray
Exists in
%span
- branch = commit_default_branch(@project, @branches)
= link_to(branch, project_tree_path(@project, branch))
- if @branches.any?
and in
= link_to("#{pluralize(@branches.count, "other branch")}", "#", class: "js-details-expand")
%span.js-details-contain.hide
= commit_branches_links(@project, @branches)
.commit-box
%h3.commit-title
= gfm escape_once(@commit.title)
......
......@@ -46,7 +46,7 @@
%span= diff.old_path
- if @commit.parent_ids.present?
= link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
= link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), { class: 'btn btn-small view-file' } do
View file @
%span.commit-short-id= @commit.short_id(6)
- else
......@@ -54,7 +54,7 @@
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
= link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
= link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), { class: 'btn btn-small view-file' } do
View file @
%span.commit-short-id= @commit.short_id(6)
......
......@@ -11,7 +11,7 @@
%fieldset
.control-group.project_name_holder
= f.label :name do
Project name is
Project name
.controls
= f.text_field :name, placeholder: "Example Project", class: "span5"
......@@ -21,7 +21,7 @@
Project description
%span.light (optional)
.controls
= f.text_area :description, placeholder: "awesome project", class: "span5", rows: 3, maxlength: 250
= f.text_area :description, placeholder: "Awesome project", class: "span5", rows: 3, maxlength: 250
- if @project.repository.exists? && @project.repository.branch_names.any?
.control-group
......@@ -67,7 +67,7 @@
- if Project.issues_tracker.values.count > 1
.control-group
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
.controls= f.select(:issues_tracker, project_issues_trackers, {}, { disabled: !@project.issues_enabled })
.controls= f.select(:issues_tracker, project_issues_trackers(@project.issues_tracker), {}, { disabled: !@project.issues_enabled })
.control-group
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
......@@ -124,7 +124,7 @@
%span Namespace
.controls
.control-group
= f.select :namespace_id, namespaces_options(@project.namespace_id), {prompt: 'Choose a project namespace'}, {class: 'chosen'}
= f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'chosen' }
%ul
%li Be careful. Changing the project's namespace can have unintended side effects.
%li You can only transfer the project to namespaces you manage.
......@@ -144,7 +144,9 @@
%span Path
.controls
.control-group
= f.text_field :path
.input-append
= f.text_field :path
%span.add-on .git
%ul
%li Be careful. Renaming a project's repository can have unintended side effects.
%li You will need to update your local repositories to point to the new location.
......
= render 'clone_panel'
%h3.page-title
= @project.name_with_namespace
.form-horizontal.pull-right
= render "shared/clone_panel"
- if @project.import? && !@project.imported
.save-project-loader
......
......@@ -3,9 +3,9 @@
%i.icon-code-fork
Fork Error!
%p
You are trying to fork
You tried to fork
= link_to_project @project
but it fails due to next reason:
but it failed for the following reason:
- if @forked_project && @forked_project.errors.any?
......
- frequency = @project.issues.tagged_with(label.name).count
%li
%strong
%span{class: "label #{label_css_class(label.name)}"}
%i.icon-tag
- if frequency.zero?
%span.light= label.name
- else
= label.name
%span{class: "label #{label_css_class(label.name)}"}
%i.icon-tag
- if frequency.zero?
%span.light= label.name
- else
= label.name
.pull-right
- unless frequency.zero?
= link_to project_issues_path(label_name: label.name) do
%strong
= pluralize(frequency, 'issue')
= "»"
= pluralize(frequency, 'issue')
= "»"
......@@ -13,7 +13,6 @@
= f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'})
.pull-left
&nbsp;
%i.icon-code-fork
= f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'})
.mr_source_commit.prepend-top-10
.span2
......@@ -26,7 +25,6 @@
= f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span3'})
.pull-left
&nbsp;
%i.icon-code-fork
= f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'})
.mr_target_commit.prepend-top-10
......
......@@ -13,13 +13,14 @@
.ui-box-body
%div
%cite.cgray
Created at #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)}
Created on #{@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)}
Currently assigned to #{link_to_member(@project, @merge_request.assignee)}.
- if @merge_request.milestone
- milestone = @merge_request.milestone
%cite.cgray and attached to milestone
%cite.cgray Attached to milestone
%strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
\.
- if @merge_request.description.present?
......
......@@ -6,7 +6,7 @@
= form_for @project, remote: true do |f|
.control-group.project-name-holder
= f.label :name do
%strong Project name is
%strong Project name
.controls
= f.text_field :name, placeholder: "Example Project", class: "input-xlarge", tabindex: 1, autofocus: true
%span.help-inline
......@@ -46,7 +46,7 @@
Description
%span.light (optional)
.controls
= f.text_area :description, placeholder: "awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3
= f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3
.control-group.project-public-holder
= f.label :public do
%span Public project
......
%h3.page-title New file
%hr
.file-editor
= form_tag(project_new_tree_path(@project, @id), method: :put, class: "form-horizontal") do
.control-group.commit_message-group
= label_tag 'file_name', class: "control-label" do
File name
.controls
%span.monospace= @path[-1] == "/" ? @path : @path + "/"
&nbsp;
= text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true
%span
&nbsp;
on
%span.label-branch= @ref
.control-group.commit_message-group
= label_tag 'commit_message', class: "control-label" do
Commit message
.controls
= text_area_tag 'commit_message', params[:commit_message], placeholder: "Added new file", required: true, rows: 3
.file-holder
.file-title
%i.icon-file
.file-content.code
%pre#editor= params[:content]
.form-actions
= hidden_field_tag 'content', '', id: "file-content"
.commit-button-annotation
= button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-create'
.message
to branch
%strong= @ref
= link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", confirm: leave_edit_message
:javascript
ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
var editor = ace.edit("editor");
disableButtonIfEmptyField("#commit_message", ".js-commit-button");
$(".js-commit-button").click(function(){
$("#file-content").val(editor.getValue());
$(".file-editor form").submit();
});
%h3.page-title
- if @service.activated?
%span.cgreen
%i.icon-circle
- else
%span.cgray
%i.icon-circle-blank
= @service.title
= boolean_to_icon @service.activated?
%p= @service.description
......
......@@ -6,12 +6,8 @@
- @services.each do |service|
%li
%h4
- if service.activated?
%span.cgreen
%i.icon-circle
- else
%span.cgray
%i.icon-circle-blank
= link_to edit_project_service_path(@project, service.to_param) do
= service.title
.pull-right
= boolean_to_icon service.activated?
%p= service.description
= render 'clone_panel'
.project-home-panel
.row
.span4
%h4.project-home-title
= @project.name_with_namespace
- if @project.public
%span.public-label Public
- else
%span.public-label Private
.span8
.project-home-dropdown
= render "dropdown"
.form-horizontal
= render "shared/clone_panel"
.project-home-extra.clearfix
.project-home-desc
- if @project.description.present?
= @project.description
- if can?(current_user, :admin_project, @project)
&ndash;
%strong= link_to 'Edit', edit_project_path
.project-home-links
= link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
= link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)
= link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)
%span.light.prepend-left-20= repository_size
.row
.span9
......@@ -6,20 +34,34 @@
= render 'shared/event_filter'
.content_list
.loading.hide
.span3
.light-well
%h3.page-title
= @project.name
- if @project.description.present?
%p.light= @project.description
.span3.project-side
.clearfix
- if @project.forked_from_project
.alert.alert-success
%i.icon-code-fork.project-fork-icon
Forked from:
%br
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
- if current_user.already_forked?(@project)
= link_to project_path(current_user.fork_of(@project)), class: 'btn btn-block' do
%i.icon-compass
Go to fork
- else
= link_to fork_project_path(@project), title: "Fork", class: "btn btn-block", method: "POST" do
%i.icon-code-fork
Fork repository
%hr
- if can? current_user, :download_code, @project
= link_to archive_project_repository_path(@project), class: "btn btn-block" do
%i.icon-download-alt
%span Download
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do
Compare code
.prepend-top-10
%p
%p
%span.light Repo size is
= repository_size
%p
%span.light Created at
%span.light Created on
#{@project.created_at.stamp('Aug 22, 2013')}
%p
%span.light Owned by
......@@ -27,19 +69,7 @@
#{link_to @project.group.name, @project.group} Group
- else
#{link_to @project.owner_name, @project.owner}
- if @project.forked_from_project
%p
%i.icon-code-fork
Forked from:
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
%hr
%p
= link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
%p
= link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)
%p
= link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)
- if @project.gitlab_ci?
%hr
......
......@@ -10,6 +10,12 @@
= link_to truncate(title, length: 40), project_tree_path(@project, path)
- else
= link_to title, '#'
- if @repository.branch_names.include?(@ref)
\/
%li
= link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
%small
%i.icon-plus.light
%div#tree-content-holder.tree-content-holder
%table#tree-slider{class: "table_#{@hex_path} tree-table" }
......
......@@ -2,35 +2,31 @@
%h3.page-title
Git access for
%strong= @gollum_wiki.path_with_namespace
= render 'main_links'
.content
.project_clone_panel
.row
.span7
.form-horizontal
.input-prepend.project_clone_holder
%button{class: "btn active", :"data-clone" => @gollum_wiki.ssh_url_to_repo} SSH
%button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= gitlab_config.protocol.upcase
= text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
.git-empty
%fieldset
%legend Install Gollum:
%pre.dark
:preserve
gem install gollum
.form-horizontal.pull-right
.git-clone-holder
%button{class: "btn active", :"data-clone" => @gollum_wiki.ssh_url_to_repo} SSH
%button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= gitlab_config.protocol.upcase
= text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
%legend Clone Your Wiki:
%pre.dark
:preserve
git clone #{@gollum_wiki.ssh_url_to_repo}
cd #{@gollum_wiki.path}
.git-empty
%fieldset
%legend Install Gollum:
%pre.dark
:preserve
gem install gollum
%legend Start Gollum And Edit Locally:
%pre.dark
:preserve
gollum
== Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop
%legend Clone Your Wiki:
%pre.dark
:preserve
git clone #{@gollum_wiki.ssh_url_to_repo}
cd #{@gollum_wiki.path}
%legend Start Gollum And Edit Locally:
%pre.dark
:preserve
gollum
== Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop
.search_results
%ul.bordered-list
= render partial: "search/results/project", collection: @projects
= render partial: "search/results/merge_request", collection: @merge_requests
= render partial: "search/results/issue", collection: @issues
%ul.nav.nav-pills
%li{class: ("active" if params[:search_code].present?)}
= link_to search_path(params.merge(search_code: true)) do
Repository Code
%li{class: ("active" if params[:search_code].blank?)}
= link_to search_path(params.merge(search_code: nil)) do
Everything else
.search_results
- if params[:search_code].present?
.blob-results
= render partial: "search/results/blob", collection: @blobs
= paginate @blobs, theme: 'gitlab'
- else
%ul.bordered-list
= render partial: "search/results/merge_request", collection: @merge_requests
= render partial: "search/results/issue", collection: @issues
%fieldset
%legend
Search results
%span.cgray (#{@total_results})
- if @project
%ul.nav.nav-pills
%li{class: ("active" if params[:search_code].present?)}
= link_to search_path(params.merge(search_code: true)) do
Repository Code
%li{class: ("active" if params[:search_code].blank?)}
= link_to search_path(params.merge(search_code: nil)) do
Everything else
.search_results
%ul.bordered-list
- @projects.each do |project|
%li
project:
= link_to project do
%strong.term= project.name_with_namespace
- @merge_requests.each do |merge_request|
%li
merge request:
= link_to [merge_request.target_project, merge_request] do
%span ##{merge_request.iid}
%strong.term
= truncate merge_request.title, length: 50
- if merge_request.for_fork?
%span.light (#{merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} &rarr; #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch})
- else
%span.light (#{merge_request.source_branch} &rarr; #{merge_request.target_branch})
- if merge_request.closed?
%span.label Closed
- @issues.each do |issue|
%li
issue:
= link_to [issue.project, issue] do
%span ##{issue.iid}
%strong.term
= truncate issue.title, length: 50
%span.light (#{issue.project.name_with_namespace})
- if issue.closed?
%span.label Closed
- @wiki_pages.each do |wiki_page|
%li
wiki:
= link_to project_wiki_path(wiki_page.project, wiki_page) do
%strong.term
= truncate wiki_page.title, length: 50
%span.light (#{wiki_page.project.name_with_namespace})
- @blobs.each do |blob|
= render 'blob', blob: blob
= paginate @blobs, theme: 'gitlab'
:javascript
$(".search_results .term").highlight("#{escape_javascript(params[:search])}");
%fieldset
%legend
Search results
%span.cgray (#{@total_results})
- if @project
= render "project_results"
- else
= render "global_results"
:javascript
$(".search_results .term").highlight("#{escape_javascript(params[:search])}");
%li
.blob-result
.file-holder
.file-title
= link_to project_blob_path(@project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do
......
%li
issue:
= link_to [issue.project, issue] do
%span ##{issue.iid}
%strong.term
= truncate issue.title, length: 50
%span.light (#{issue.project.name_with_namespace})
- if issue.closed?
%span.label Closed
%li
merge request:
= link_to [merge_request.target_project, merge_request] do
%span ##{merge_request.iid}
%strong.term
= truncate merge_request.title, length: 50
- if merge_request.for_fork?
%span.light (#{merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} &rarr; #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch})
- else
%span.light (#{merge_request.source_branch} &rarr; #{merge_request.target_branch})
- if merge_request.closed?
%span.label Closed
%li
project:
= link_to project do
%strong.term= project.name_with_namespace
- if project.description.present?
&ndash;
%span.light.term= project.description
......@@ -13,4 +13,4 @@
.results.prepend-top-10
- if params[:search].present?
= render 'search/result'
= render 'search/results'
.input-prepend.input-append.project_clone_holder
.git-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.protocol.upcase
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span7", readonly: true
%span.add-on
- if @project.public
= public_icon
%span.cblue public
- else
= private_icon
%span.cgreen private
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5", readonly: true
- if current_user.require_ssh_key? && alert.blank? && notice.blank?
%p.error-message.centered
You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile
- if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key?
.no-ssh-key-message
.container
You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile
= link_to '#', class: 'pull-right hide-no-ssh-message' do
%i.icon-remove
......@@ -8,9 +8,18 @@
= link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet'
= link_to "Delete", snippet_path(@snippet), method: :delete, confirm: "Are you sure?", class: "btn btn-tiny", title: 'Delete Snippet'
= link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank"
.file-content.code
- unless @snippet.content.empty?
%div{class: user_color_scheme_class}
= raw @snippet.colorize(formatter: :gitlab)
- unless @snippet.content.empty?
- if gitlab_markdown?(@snippet.file_name)
.file-content.wiki
= preserve do
= markdown(@snippet.data)
- elsif markup?(@snippet.file_name)
.file-content.wiki
= raw GitHub::Markup.render(@snippet.file_name, @snippet.data)
- else
.file-content.code
%div{class: user_color_scheme_class}
= raw @snippet.colorize(formatter: :gitlab)
- else
.file-content.code
%p.nothing_here_message Empty file
......@@ -13,9 +13,20 @@
= f.label :title
.controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
.control-group
= f.label "Private?"
= f.label "Access"
.controls
= f.check_box :private, {class: ''}
= f.label :private_true, class: 'radio-label' do
= f.radio_button :private, true
%span
%strong Private
(only you can see this snippet)
%br
= f.label :private_false, class: 'radio-label' do
= f.radio_button :private, false
%span
%strong Public
(GitLab users can can see this snippet)
.control-group
.file-editor
= f.label :file_name, "File"
......@@ -33,9 +44,10 @@
- else
= f.submit 'Save', class: "btn-save btn"
= link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
- unless @snippet.new_record?
.pull-right= link_to 'Destroy', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
.pull-right.prepend-left-20
= link_to 'Remove', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn btn-remove delete-snippet", id: "destroy_snippet_#{@snippet.id}"
= link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
:javascript
......
......@@ -3,7 +3,7 @@
= link_to reliable_snippet_path(snippet) do
= truncate(snippet.title, length: 60)
- if snippet.private?
%span.label.label-success
%span.label.label-gray
%i.icon-lock
private
%span.cgray.monospace.tiny.pull-right
......
......@@ -14,7 +14,6 @@ class RepositoryImportWorker
project.imported = true
project.save
project.satellite.create unless project.satellite.exists?
project.discover_default_branch
else
project.imported = false
end
......
......@@ -78,7 +78,6 @@ module Gitlab
#
# config.relative_url_root = "/gitlab"
# Uncomment to enable rack attack middleware
# config.middleware.use Rack::Attack
config.middleware.use Rack::Attack
end
end
......@@ -57,9 +57,9 @@ production: &base
## Automatic issue closing
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
# This happends when the commit is pushed or merged into the default branch of a project.
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# issue_closing_pattern: ([Cc]loses|[Ff]ixes) +#\d+
# issue_closing_pattern: ([Cc]lose[sd]|[Ff]ixe[sd]) +#\d+
## Default project features settings
default_projects_features:
......@@ -155,7 +155,7 @@ production: &base
## Auth providers
# Uncomment the following lines and fill in the data of the auth provider you want to use
# If your favorite auth provider is not listed you can use others:
# see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
# see https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-custom-omniauth-provider-configurations
# The 'app_id' and 'app_secret' parameters are always passed as the first two
# arguments, followed by optional 'args' which can be either a hash or an array.
providers:
......
# To enable rack-attack for your GitLab instance do the following:
# 1. In config/application.rb find and uncomment the following line:
# config.middleware.use Rack::Attack
# 2. Rename this file to rack_attack.rb
# 3. Review the paths_to_be_protected and add any other path you need protecting
# 4. Restart GitLab instance
# 1. Rename this file to rack_attack.rb
# 2. Review the paths_to_be_protected and add any other path you need protecting
#
paths_to_be_protected = [
......@@ -11,6 +7,9 @@ paths_to_be_protected = [
"#{Rails.application.config.relative_url_root}/users/sign_in",
"#{Rails.application.config.relative_url_root}/users"
]
Rack::Attack.throttle('protected paths', limit: 6, period: 60.seconds) do |req|
req.ip if paths_to_be_protected.include?(req.path) && req.post?
unless Rails.env.test?
Rack::Attack.throttle('protected paths', limit: 6, period: 60.seconds) do |req|
req.ip if paths_to_be_protected.include?(req.path) && req.post?
end
end
......@@ -2,6 +2,7 @@
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
servers: Gitlab::Application.config.cache_store.last, # re-use the Redis config from the Rails cache store
key: '_gitlab_session',
secure: Gitlab::Application.config.force_ssl,
httponly: true,
......
......@@ -3,7 +3,7 @@
# 2. Edit settings inside this file
# 3. Restart GitLab instance
#
if Rails.env.production? do
if Rails.env.production?
Gitlab::Application.config.action_mailer.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
......
......@@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do
get :test
end
resources :broadcast_messages, only: [:index, :create, :destroy]
resource :logs, only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show]
resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do
member do
put :transfer
end
end
root to: "dashboard#index"
end
......@@ -166,16 +173,18 @@ Gitlab::Application.routes.draw do
end
scope module: :projects do
resources :blob, only: [:show], constraints: {id: /.+/}
resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
resources :compare, only: [:index, :create]
resources :blame, only: [:show], constraints: {id: /.+/}
resources :blob, only: [:show, :destroy], constraints: {id: /.+/}
resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
resources :compare, only: [:index, :create]
resources :blame, only: [:show], constraints: {id: /.+/}
resources :network, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
resources :snippets, constraints: {id: /\d+/} do
......@@ -222,14 +231,14 @@ Gitlab::Application.routes.draw do
end
end
resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do
resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do
collection do
get :recent
get :recent, constraints: { id: Gitlab::Regex.git_reference_regex }
end
end
resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }
resources :protected_branches, only: [:index, :create, :destroy], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }
resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
resources :protected_branches, only: [:index, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
resources :refs, only: [] do
collection do
......@@ -238,11 +247,11 @@ Gitlab::Application.routes.draw do
member do
# tree viewer logs
get "logs_tree", constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }
get "logs_tree", constraints: { id: Gitlab::Regex.git_reference_regex }
get "logs_tree/:path" => "refs#logs_tree",
as: :logs_file,
constraints: {
id: /[a-zA-Z.0-9\/_\-#%+]+/,
id: Gitlab::Regex.git_reference_regex,
path: /.*/
}
end
......@@ -286,6 +295,7 @@ Gitlab::Application.routes.draw do
resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do
collection do
delete :leave
# Used for import team
# from another project
......
class RemoveDefaultBranch < ActiveRecord::Migration
def up
remove_column :projects, :default_branch
end
def down
add_column :projects, :default_branch, :string
end
end
class CreateBroadcastMessages < ActiveRecord::Migration
def change
create_table :broadcast_messages do |t|
t.text :message, null: false
t.datetime :starts_at
t.datetime :ends_at
t.integer :alert_type
t.timestamps
end
end
end
......@@ -11,7 +11,16 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20131009115346) do
ActiveRecord::Schema.define(:version => 20131112114325) do
create_table "broadcast_messages", :force => true do |t|
t.text "message", :null => false
t.datetime "starts_at"
t.datetime "ends_at"
t.integer "alert_type"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "deploy_keys_projects", :force => true do |t|
t.integer "deploy_key_id", :null => false
......@@ -181,7 +190,6 @@ ActiveRecord::Schema.define(:version => 20131009115346) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "creator_id"
t.string "default_branch"
t.boolean "issues_enabled", :default => true, :null => false
t.boolean "wall_enabled", :default => true, :null => false
t.boolean "merge_requests_enabled", :default => true, :null => false
......
......@@ -2,7 +2,7 @@
### List projects
Get a list of projects owned by the authenticated user.
Get a list of projects accessible by the authenticated user.
```
GET /projects
......@@ -82,6 +82,22 @@ GET /projects
```
#### List owned projects
Get a list of projects owned by the authenticated user.
```
GET /projects/owned
```
#### List ALL projects
Get a list of all GitLab projects (admin only).
```
GET /projects/all
```
### Get single project
Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user.
......@@ -213,7 +229,6 @@ Parameters:
+ `name` (required) - new project name
+ `description` (optional) - short project description
+ `default_branch` (optional) - 'master' by default
+ `issues_enabled` (optional)
+ `wall_enabled` (optional)
+ `merge_requests_enabled` (optional)
......
......@@ -368,4 +368,43 @@ GET /projects/:id/repository/archive
Parameters:
+ `id` (required) - The ID of a project
+ `sha` (optional) - The commit sha to download defaults to the tip of the default branch
\ No newline at end of file
+ `sha` (optional) - The commit sha to download defaults to the tip of the default branch
## Create new file in repository
```
POST /projects/:id/repository/files
```
Parameters:
+ `file_path` (optional) - Full path to new file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `content` (required) - File content
+ `commit_message` (required) - Commit message
## Update existing file in repository
```
PUT /projects/:id/repository/files
```
Parameters:
+ `file_path` (required) - Full path to file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `content` (required) - New file content
+ `commit_message` (required) - Commit message
## Delete existing file in repository
```
DELETE /projects/:id/repository/files
```
Parameters:
+ `file_path` (required) - Full path to file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `commit_message` (required) - Commit message
......@@ -52,7 +52,7 @@ If you are not familiar with vim please skip this and keep using the default edi
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 curl git-core openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate
sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate
Make sure you have the right version of Python installed.
......@@ -74,6 +74,33 @@ Make sure you have the right version of Python installed.
# For reStructuredText markup language support install required package:
sudo apt-get install -y python-docutils
Make sure you have the right version of Git installed
# Install Git
sudo apt-get install -y git-core
# Make sure Git is version 1.7.10 or higher, for example 1.7.12 or 1.8.4
git --version
Is the system packaged Git too old? Remove it and compile from source.
# Remove packaged Git
sudo apt-get remove git-core
# Install dependencies
sudo apt-get install -y libcurl4-openssl-dev libexpat1-dev gettext libz-dev libssl-dev build-essential
# Download and compile from source
cd /tmp
curl --progress https://git-core.googlecode.com/files/git-1.8.4.1.tar.gz | tar xz
cd git-1.8.4.1/
make prefix=/usr/local all
# Install into /usr/local/bin
sudo make prefix=/usr/local install
# When editing config/gitlab.yml (Step 6), change the git bin_path to /usr/local/bin/git
**Note:** In order to receive mail notifications, make sure to install a
mail server. By default, Debian is shipped with exim4 whereas Ubuntu
does not ship with one. The recommended mail server is postfix and you can install it with:
......@@ -91,8 +118,8 @@ Remove the old Ruby 1.8 if present
Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby
curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p247.tar.gz | tar xz
cd ruby-2.0.0-p247
curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz
cd ruby-2.0.0-p353
./configure --disable-install-rdoc
make
sudo make install
......@@ -122,7 +149,7 @@ GitLab Shell is an ssh access and repository management software developed speci
cd gitlab-shell
# switch to right version
sudo -u git -H git checkout v1.7.8
sudo -u git -H git checkout v1.7.9
sudo -u git -H cp config.yml.example config.yml
......@@ -153,10 +180,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
cd /home/git/gitlab
# Checkout to stable release
sudo -u git -H git checkout 6-2-stable-ee
sudo -u git -H git checkout 6-3-stable-ee
**Note:**
You can change `6-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
You can change `6-3-stable-ee` to `master` if you want the *bleeding edge* version, but never install master on a production server!
## Configure it
......@@ -167,6 +194,8 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version,
# Make sure to change "localhost" to the fully-qualified domain name of your
# host serving GitLab where necessary
#
# If you installed Git from source, change the git bin_path to /usr/local/bin/git
sudo -u git -H editor config/gitlab.yml
# Make sure GitLab can write to the log/ and tmp/ directories
......@@ -198,10 +227,6 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version,
# Copy the example Rack attack config
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
# Enable rack attack middleware
# Find and uncomment the line 'config.middleware.use Rack::Attack'
sudo -u git -H editor config/application.rb
# Configure Git global settings for git user, useful when editing via web
# Edit user.email according to what is set in gitlab.yml
sudo -u git -H git config --global user.name "GitLab"
......@@ -236,8 +261,6 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
cd /home/git/gitlab
sudo gem install charlock_holmes --version '0.6.9.4'
# For MySQL (note, the option says "without ... postgres")
sudo -u git -H bundle install --deployment --without development test postgres aws
......
# Operating Systems
## Linux
GitLab is developed for the Linux operating system.
GitLab officially supports (recent versions of) these Linux distributions:
## GitLab officially supports
- Ubuntu Linux
- Debian/GNU Linux
It should also work on (though they are not officially supported):
## GitLab.com offers paid support for
- Arch
- Red Hat Enterprise Linux (RHEL)
- CentOS
- Oracle Linux
## Not officially supported
- Arch Linux
- Fedora
- Gentoo
- RedHat
## Other Unix Systems
On the above distributions it is pretty easy to install GitLab yourself.
## Unsupported Unix Systems
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!**
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 do this, please be aware it could be a lot of work.
Please consider using a virtual machine to run GitLab.
## Windows
## Other operating systems such as Windows
GitLab does **not** run on Windows and we have no plans of supporting it in the
near future. Please consider using a virtual machine to run GitLab.
GitLab does **not** run on Windows and we have no plans of supporting it in the near future.
Please consider using a virtual machine to run GitLab.
# Rubies
# Ruby versions
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.
GitLab requires Ruby (MRI) 1.9.3 or 2.0+.
While it is generally possible to use other Rubies
(like [JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/))
it might require some work since GitLab uses several Gems that have native extensions.
# Hardware requirements
......
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions.
1. Definitions.
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"Contribution" shall mean the code, documentation or other original works of authorship expressly identified in Schedule B, as well as any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation.
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others).
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
8. It is your responsibility to notify GitLab.com when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab.com.
---------------------------------------
This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions.
1. Definitions.
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to GitLab.com, or that your employer has executed a separate Corporate CLA with GitLab.com.
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [[]named here]".
8. You agree to notify GitLab.com of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
---------------------------------------
This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
# Things to do when creating new release
# Things to do when creating new monthly minor or major release
NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
## Install guide up to date?
......@@ -58,13 +58,18 @@ Check if changed since last release (~22nd of last month depending on when last
After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked.
- 5 days before release: feature freeze
- 3 days before release: UI freeze
- 1 day before release: code freeze
* 5 days before release: feature freeze
* 3 days before release: UI freeze
* 1 day before release: code freeze
# Write a blog post
* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
* Select and thank the the Most Valuable Person (MVP) of this release.
* Note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
## Last actions
1. Write a blog post (mention what GitLab is on the first line, select a MVP)
1. Update VERSION and CHANGELOG
1. Create a git tag vX.X.X
1. Publish the blog post
......
# Things to do when doing an out-of-bound security release
NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
## When to do a security release
Do a security release when there is a critical issue that needs to be adresses before the next monthly release. Otherwise include it in the monthly release and note there was a security fix in the release announcement.
## Security vulnerability disclosure
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
## Release Procedure
1. Verify that the issue can be repoduced
1. Acknowledge the issue to the researcher that disclosed it
1. Fix the issue on a feature branch, do this on the private GitLab development server and update the VERSION and CHANGELOG in this branch
1. Consider creating and testing workarounds
1. Create feature branches for the blog posts on GitLab.org and GitLab.com and link them from the code branch
1. Merge the code feature branch into master
1. Cherry-pick the code into the latest stable branch
1. Create a git tag vX.X.X for CE and another patch release for EE
1. Push the code and the tags to all the CE and EE repositories
1. Apply the patch to GitLab Cloud and the private GitLab development server
1. Merge and publish the blog posts
1. Send tweets about the release from @gitlabhq and @git_lab
1. Send out an email to the subscribers mailing list on MailChimp
1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq)
1. Send out an email to [the GitLab newsletter list](http://gitlab.us5.list-manage.com/subscribe?u=498dccd07cf3e9482bee33ba4&id=98a9a4992c)
1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number
1. Add the security researcher to the [Security Researcher Acknowledgments list](http://www.gitlab.com/vulnerability-acknowledgements/)
1. Thank the security researcher in an email for their cooperation
1. Update the blogpost and the CHANGELOG when we receive the CVE number
The timing of the code merge into master should be coordinated in advance.
After the merge we strive to publish the announcements within 60 minutes.
## Blog post template
XXX Security Advisory for GitLab
A recently discovered critical vulnerability in GitLab allows [unauthenticated API access|remote code execution|unauthorized access to repositories|XXX|PICKSOMETHING]. All users should update GitLab and gitlab-shell immediately.
We [have|haven't|XXX|PICKSOMETHING|] heard of this vulnerability being actively exploited.
### Version affected
GitLab Community Edition XXX and lower
GitLab Enterprise Edition XXX and lower
### Fixed versions
GitLab Community Edition XXX and up
GitLab Enterprise Edition XXX and up
### Impact
On GitLab installations which use MySQL as their database backend it is possible for an attacker to assume the identity of any existing GitLab user in certain API calls. This attack can be performed by [unauthenticated|authenticated|XXX|PICKSOMETHING] users.
### Workarounds
If you are unable to upgrade you should apply the following patch and restart GitLab.
XXX
### Credit
We want to thank XXX of XXX for the reponsible disclosure of this vulnerability.
## Email template
We just announced a security advisory for GitLab at XXX
Please contact us at support@gitlab.com if you have any questions.
## Tweet template
We just announced a security advisory for GitLab at XXX
# From 5.1 to 5.4
Also works starting from 5.2.
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.
### 0. Backup
It's useful to make a backup just in case things go south:
(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
sudo service gitlab stop
### 2. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch
sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses CVE-2013-4489
```
### 3. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
```
### 4. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL
sudo -u git -H bundle install --without development test postgres --deployment
#PostgreSQL
sudo -u git -H bundle install --without development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
```
### 5. Update config files
* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/5-4-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/puma.rb` same as https://github.com/gitlabhq/gitlabhq/blob/5-4-stable/config/puma.rb.example but with your settings.
### 6. Update Init script
```bash
sudo rm /etc/init.d/gitlab
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
### 7. Create uploads directory
```bash
cd /home/git/gitlab
sudo -u git -H mkdir public/uploads
sudo chmod -R u+rwX public/uploads
```
### 8. Start application
sudo service gitlab start
sudo service nginx restart
### 9. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade complete!
## Things went south? Revert to previous version (5.3)
### 1. Revert the code to the previous version
Follow the [`upgrade guide from 5.2 to 5.3`](5.2-to-5.3.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
# From 5.1 to 6.0
## Warning
GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 immediately](6.0-to-6.2.md).
### Deprecations
#### Global projects
The root (global) namespace for projects is deprecated.
So you need to move all your global projects under groups or users manually before update or they will be automatically moved to the project owner namespace during the update. When a project is moved all its members will receive an email with instructions how to update their git remote url. Please make sure you disable sending email when you do a test of the upgrade.
#### Teams
We introduce group membership in 6.0 as a replacement for teams.
The old combination of groups and teams was confusing for a lot of people.
And when the members of a team where changed this wasn't reflected in the project permissions.
In GitLab 6.0 you will be able to add members to a group with a permission level for each member.
These group members will have access to the projects in that group.
Any changes to group members will immediately be reflected in the project permissions.
You can even have multiple owners for a group, greatly simplifying administration.
### 0. Backup
It's useful to make a backup just in case things go south:
(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
sudo service gitlab stop
### 2. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch
sudo -u git -H git checkout 6-0-stable
```
### 3. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.7.9
```
### 4. Install additional packages
```bash
# For reStructuredText markup language support install required package:
sudo apt-get install python-docutils
```
### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL
sudo -u git -H bundle install --without development test postgres --deployment
#PostgreSQL
sudo -u git -H bundle install --without development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production
# Clear redis cache
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
# Clear and precompile assets
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
```
### 6. Update config files
Note: We switched from Puma in GitLab 5.x to unicorn in GitLab 6.0.
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://github.com/gitlabhq/gitlabhq/blob/master/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://github.com/gitlabhq/gitlabhq/blob/master/config/unicorn.rb.example but with your settings.
### 7. Update Init script
```bash
cd /home/git/gitlab
sudo rm /etc/init.d/gitlab
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
### 8. Create uploads directory
```bash
cd /home/git/gitlab
sudo -u git -H mkdir -p public/uploads
sudo chmod -R u+rwX public/uploads
```
### 9. Start application
sudo service gitlab start
sudo service nginx restart
### 10. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade complete!
## Things went south? Revert to previous version (5.1)
### 1. Revert the code to the previous version
Follow the [`upgrade guide from 5.0 to 5.1`](5.0-to-5.1.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
### Troubleshooting
The migrations in this update are very sensitive to incomplete or inconsistent data. If you have a long-running GitLab installation and some of the previous upgrades did not work out 100% correct this may bite you now. The following commands can be run in the rails console to look for 'bad' data.
All project owners should have an owner
```
Project.all.select { |project| project.owner.blank? }
```
Every user should have a namespace
```
User.all.select { |u| u.namespace.blank? }
```
Projects in the global namespace should not conflict with projects in the owner namespace
```
Project.where(namespace_id: nil).select { |p| Project.where(path: p.path, namespace_id: p.owner.try(:namespace).try(:id)).present? }
```
# From 6.0 to 6.2
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 6.2.
# In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.
### Deprecations
#### Global issue numbers
As of 6.1 issue numbers are project specific. This means all issues are renumbered and get a new number in their url. If you use an old issue number url and the issue number does not exist yet you are redirected to the new one. This conversion does not trigger if the old number already exists for this project, this is unlikely but will happen with old issues and large projects.
### 0. Backup
It's useful to make a backup just in case things go south:
(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
sudo service gitlab stop
### 2. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch
sudo -u git -H git checkout 6-2-stable # Latest version of 6-2-stable addresses CVE-2013-4489
```
### 3. Install additional packages
```bash
# Add support for lograte for better log file handling
sudo apt-get install logrotate
```
### 4. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
```
### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL
sudo -u git -H bundle install --without development test postgres --deployment
#PostgreSQL
sudo -u git -H bundle install --without development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
### 6. Update config files
TIP: to see what changed in gitlab.yml.example in this release use next command:
```
git diff 6-0-stable:config/gitlab.yml.example 6-2-stable:config/gitlab.yml.example
```
* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/unicorn.rb.example but with your settings.
* Copy rack attack middleware config
```bash
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
```
* Uncomment `config.middleware.use Rack::Attack` in `/home/git/gitlab/config/application.rb`
* Set up logrotate
```bash
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
```
### 7. Update Init script
```bash
sudo rm /etc/init.d/gitlab
sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6-2-stable/lib/support/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
### 8. Start application
sudo service gitlab start
sudo service nginx restart
### 9. Check application status
Check if GitLab and its environment are configured correctly:
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade complete!
## Things went south? Revert to previous version (6.0)
### 1. Revert the code to the previous version
Follow the [`upgrade guide from 5.4 to 6.0`](5.4-to-6.0.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
# From 6.2 to 6.3
## Requires version: 6.1 or 6.2
### 0. Backup
It's useful to make a backup just in case things go south:
(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
sudo service gitlab stop
### 2. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch
sudo -u git -H git checkout 6-3-stable
```
### 3. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
```
### 4. Install additional packages
```bash
# Add support for lograte for better log file handling
sudo apt-get install logrotate
```
### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL
sudo -u git -H bundle install --without development test postgres --deployment
#PostgreSQL
sudo -u git -H bundle install --without development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
### 6. Update config files
TIP: to see what changed in gitlab.yml.example in this release use next command:
```
git diff 6-2-stable:config/gitlab.yml.example 6-3-stable:config/gitlab.yml.example
git diff 6-1-stable:config/gitlab.yml.example 6-3-stable:config/gitlab.yml.example # if you upgrading from 6-1
```
* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-3-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` same as https://github.com/gitlabhq/gitlabhq/blob/6-3-stable/config/unicorn.rb.example but with your settings.
* Copy rack attack middleware config
```bash
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
```
* Set up logrotate
```bash
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
```
### 7. Update Init script
```bash
sudo rm /etc/init.d/gitlab
sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6-3-stable/lib/support/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
### 8. Start application
sudo service gitlab start
sudo service nginx restart
### 9. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade complete!
## Things went south? Revert to previous version (6.2)
### 1. Revert the code to the previous version
Follow the [`upgrade guide from 6.1 to 6.2`](6.1-to-6.2.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
# Universal update guide for patch versions. Ex. from From 6.2.0 to 6.2.1
### 0. Backup
It's useful to make a backup just in case things go south:
(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
sudo service gitlab stop
### 2. Get latest code for your current stable branch
```bash
cd /home/git/gitlab
sudo -u git -H git pull origin 6-2-stable
```
### 3. Update gitlab-shell if necessary
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.7.9
```
### 4. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL
sudo -u git -H bundle install --without development test postgres --deployment
#PostgreSQL
sudo -u git -H bundle install --without development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
### 5. Start application
sudo service gitlab start
sudo service nginx restart
### 6. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade complete!
......@@ -27,6 +27,11 @@ Feature: Admin active tab
Then the active main tab should be Logs
And no other main tabs should be active
Scenario: On Admin Messages
Given I visit admin messages page
Then the active main tab should be Messages
And no other main tabs should be active
Scenario: On Admin Hooks
Given I visit admin hooks page
Then the active main tab should be Hooks
......
Feature: Admin Broadcast Messages
Background:
Given I sign in as an admin
And application already has admin messages
And I visit admin messages page
Scenario: See broadcast messages list
Then I should be all broadcast messages
Scenario: Create a broadcast message
When submit form with new broadcast message
Then I should be redirected to admin messages page
And I should see newly created broadcast message
Feature: Project Issue Tracker
Background:
Given I sign in as a user
And I own project "Shop"
And project "Shop" has issues enabled
And I visit project "Shop" page
Scenario: I set the issue tracker to "GitLab"
When I visit edit project "Shop" page
And change the issue tracker to "GitLab"
And I save project
Then I the project should have "GitLab" as issue tracker
Scenario: I set the issue tracker to "Redmine"
When I visit edit project "Shop" page
And change the issue tracker to "Redmine"
And I save project
Then I the project should have "Redmine" as issue tracker
\ No newline at end of file
......@@ -20,6 +20,10 @@ Feature: Project Browse files
And I click link "raw"
Then I should see raw file content
Scenario: I can create file
Given I click on "new file" link in repo
Then I can see new file page
@javascript
Scenario: I can edit file
Given I click on "Gemfile.lock" file in repo
......
......@@ -30,4 +30,8 @@ class AdminActiveTab < Spinach::FeatureSteps
Then 'the active main tab should be Resque' do
ensure_active_main_tab('Background Jobs')
end
Then 'the active main tab should be Messages' do
ensure_active_main_tab('Messages')
end
end
class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedAdmin
step 'application already has admin messages' do
FactoryGirl.create(:broadcast_message, message: "Migration to new server")
end
step 'I should be all broadcast messages' do
page.should have_content "Migration to new server"
end
step 'submit form with new broadcast message' do
fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST'
select '2018', from: "broadcast_message_ends_at_1i"
click_button "Add broadcast message"
end
step 'I should be redirected to admin messages page' do
current_path.should == admin_broadcast_messages_path
end
step 'I should see newly created broadcast message' do
page.should have_content 'Application update from 4:00 CST to 5:00 CST'
end
end
......@@ -3,42 +3,51 @@ class ProjectBrowseFiles < Spinach::FeatureSteps
include SharedProject
include SharedPaths
Then 'I should see files from repository' do
step 'I should see files from repository' do
page.should have_content "app"
page.should have_content "history"
page.should have_content "Gemfile"
end
Then 'I should see files from repository for "8470d70"' do
step 'I should see files from repository for "8470d70"' do
current_path.should == project_tree_path(@project, "8470d70")
page.should have_content "app"
page.should have_content "history"
page.should have_content "Gemfile"
end
Given 'I click on "Gemfile.lock" file in repo' do
step 'I click on "Gemfile.lock" file in repo' do
click_link "Gemfile.lock"
end
Then 'I should see it content' do
step 'I should see it content' do
page.should have_content "DEPENDENCIES"
end
And 'I click link "raw"' do
step 'I click link "raw"' do
click_link "raw"
end
Then 'I should see raw file content' do
step 'I should see raw file content' do
page.source.should == ValidCommit::BLOB_FILE
end
Given 'I click button "edit"' do
step 'I click button "edit"' do
click_link 'edit'
end
Then 'I can edit code' do
step 'I can edit code' do
page.execute_script('editor.setValue("GitlabFileEditor")')
page.evaluate_script('editor.getValue()').should == "GitlabFileEditor"
end
step 'I click on "new file" link in repo' do
click_link 'new-file-link'
end
step 'I can see new file page' do
page.should have_content "New file"
page.should have_content "File name"
page.should have_content "Commit message"
end
end
class ProjectIssueTracker < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
step 'project "Shop" has issues enabled' do
@project = Project.find_by_name "Shop"
@project ||= create(:project_with_code, name: "Shop", namespace: @user.namespace)
@project.issues_enabled = true
end
step 'change the issue tracker to "GitLab"' do
select 'GitLab', from: 'project_issues_tracker'
end
step 'I the project should have "GitLab" as issue tracker' do
find_field('project_issues_tracker').value.should == 'gitlab'
end
step 'change the issue tracker to "Redmine"' do
select 'Redmine', from: 'project_issues_tracker'
end
step 'I the project should have "Redmine" as issue tracker' do
find_field('project_issues_tracker').value.should == 'redmine'
end
And 'I save project' do
click_button 'Save changes'
end
end
......@@ -109,7 +109,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end
And 'I click on the first commit in the merge request' do
click_link merge_request.commits.first.short_id(8)
end
......
......@@ -23,7 +23,7 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end
step 'public project "Community"' do
create :project_with_code, name: 'Community', public: true, default_branch: 'master'
create :project_with_code, name: 'Community', public: true
end
step 'public empty project "Empty Public Project"' do
......@@ -49,7 +49,9 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end
step 'I should see project "Community" home page' do
page.should have_content 'Repo size is'
within '.project-home-title' do
page.should have_content 'Community'
end
end
private
......
......@@ -105,6 +105,10 @@ module SharedPaths
visit admin_logs_path
end
step 'I visit admin messages page' do
visit admin_broadcast_messages_path
end
step 'I visit admin hooks page' do
visit admin_hooks_path
end
......
......@@ -47,7 +47,7 @@ module SharedProject
Then 'I should see project settings' do
current_path.should == edit_project_path(@project)
page.should have_content("Project name is")
page.should have_content("Project name")
page.should have_content("Features:")
end
......
......@@ -19,7 +19,7 @@ class SnippetsFeature < Spinach::FeatureSteps
end
And 'I click link "Destroy"' do
click_link "Destroy"
click_link "Remove"
end
And 'I submit new snippet "Personal snippet three"' do
......@@ -46,7 +46,7 @@ class SnippetsFeature < Spinach::FeatureSteps
end
And 'I uncheck "Private" checkbox' do
find(:xpath, "//input[@id='personal_snippet_private']").set true
choose "Public"
click_button "Save"
end
......
......@@ -39,5 +39,8 @@ module API
mount DeployKeys
mount ProjectHooks
mount Ldap
mount Services
mount Files
mount Namespaces
end
end
......@@ -5,16 +5,6 @@ module API
before { authorize_admin_project }
resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
end
# Get a specific project's keys
#
# Example Request:
......
......@@ -144,5 +144,9 @@ module API
class ProjectGroupLink < Grape::Entity
expose :id, :project_id, :group_id, :group_access
end
class Namespace < Grape::Entity
expose :id, :path, :kind
end
end
end
module API
# Projects API
class Files < Grape::API
before { authenticate! }
before { authorize! :push_code, user_project }
resource :projects do
# Create new file in repository
#
# Parameters:
# file_path (optional) - The path to new file. Ex. lib/class.rb
# branch_name (required) - The name of branch
# content (required) - File content
# commit_message (required) - Commit message
#
# Example Request:
# POST /projects/:id/repository/files
#
post ":id/repository/files" do
required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message]
branch_name = attrs.delete(:branch_name)
file_path = attrs.delete(:file_path)
result = ::Files::CreateContext.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success
status(201)
{
file_path: file_path,
branch_name: branch_name
}
else
render_api_error!(result[:error], 400)
end
end
# Update existing file in repository
#
# Parameters:
# file_path (optional) - The path to file. Ex. lib/class.rb
# branch_name (required) - The name of branch
# content (required) - File content
# commit_message (required) - Commit message
#
# Example Request:
# PUT /projects/:id/repository/files
#
put ":id/repository/files" do
required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message]
branch_name = attrs.delete(:branch_name)
file_path = attrs.delete(:file_path)
result = ::Files::UpdateContext.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success
status(200)
{
file_path: file_path,
branch_name: branch_name
}
else
render_api_error!(result[:error], 400)
end
end
# Delete existing file in repository
#
# Parameters:
# file_path (optional) - The path to file. Ex. lib/class.rb
# branch_name (required) - The name of branch
# content (required) - File content
# commit_message (required) - Commit message
#
# Example Request:
# DELETE /projects/:id/repository/files
#
delete ":id/repository/files" do
required_attributes! [:file_path, :branch_name, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :commit_message]
branch_name = attrs.delete(:branch_name)
file_path = attrs.delete(:file_path)
result = ::Files::DeleteContext.new(user_project, current_user, attrs, branch_name, file_path).execute
if result[:status] == :success
status(200)
{
file_path: file_path,
branch_name: branch_name
}
else
render_api_error!(result[:error], 400)
end
end
end
end
end
......@@ -6,19 +6,23 @@ module API
SUDO_PARAM = :sudo
def current_user
@current_user ||= User.find_by_authentication_token(params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER])
private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
@current_user ||= User.find_by_authentication_token(private_token)
identifier = sudo_identifier()
# If the sudo is the current user do nothing
if (identifier && !(@current_user.id == identifier || @current_user.username == identifier))
render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
@current_user = User.by_username_or_id(identifier)
not_found!("No user id or username for: #{identifier}") if @current_user.nil?
end
@current_user
end
def sudo_identifier()
identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
# Regex for integers
if (!!(identifier =~ /^[0-9]+$/))
identifier.to_i
......@@ -29,6 +33,7 @@ module API
def set_current_user_for_thread
Thread.current[:current_user] = current_user
begin
yield
ensure
......
module API
# namespaces API
class Namespaces < Grape::API
before {
authenticate!
authenticated_as_admin!
}
resource :namespaces do
# Get a namespaces list
#
# Example Request:
# GET /namespaces
get do
@namespaces = Namespace.scoped
@namespaces = @namespaces.search(params[:search]) if params[:search].present?
@namespaces = paginate @namespaces
present @namespaces, with: Entities::Namespace
end
end
end
end
......@@ -31,6 +31,16 @@ module API
present @projects, with: Entities::Project
end
# Get all projects for admin user
#
# Example Request:
# GET /projects/all
get '/all' do
authenticated_as_admin!
@projects = paginate Project
present @projects, with: Entities::Project
end
# Get a single project
#
# Parameters:
......@@ -60,7 +70,6 @@ module API
# Parameters:
# name (required) - name for new project
# description (optional) - short project description
# default_branch (optional) - 'master' by default
# issues_enabled (optional)
# wall_enabled (optional)
# merge_requests_enabled (optional)
......@@ -75,7 +84,6 @@ module API
attrs = attributes_for_keys [:name,
:path,
:description,
:default_branch,
:issues_enabled,
:wall_enabled,
:merge_requests_enabled,
......
module API
# Projects API
class Services < Grape::API
before { authenticate! }
before { authorize_admin_project }
resource :projects do
# Set GitLab CI service for project
#
# Parameters:
# token (required) - CI project token
# project_url (required) - CI project url
#
# Example Request:
# PUT /projects/:id/services/gitlab-ci
put ":id/services/gitlab-ci" do
required_attributes! [:token, :project_url]
attrs = attributes_for_keys [:token, :project_url]
user_project.build_missing_services
if user_project.gitlab_ci_service.update_attributes(attrs.merge(active: true))
true
else
not_found!
end
end
# Delete GitLab CI service settings
#
# Example Request:
# DELETE /projects/:id/keys/:id
delete ":id/services/gitlab-ci" do
if user_project.gitlab_ci_service
user_project.gitlab_ci_service.update_attributes(
active: false,
token: nil,
project_url: nil
)
end
end
end
end
end
......@@ -13,20 +13,20 @@ module Backup
def dump
case config["adapter"]
when /^mysql/ then
system("mysqldump #{mysql_args} #{config['database']} > #{db_file_name}")
system('mysqldump', *mysql_args, config['database'], out: db_file_name)
when "postgresql" then
pg_env
system("pg_dump #{config['database']} > #{db_file_name}")
system('pg_dump', config['database'], out: db_file_name)
end
end
def restore
case config["adapter"]
when /^mysql/ then
system("mysql #{mysql_args} #{config['database']} < #{db_file_name}")
system('mysql', *mysql_args, config['database'], in: db_file_name)
when "postgresql" then
pg_env
system("psql #{config['database']} -f #{db_file_name}")
system('psql', config['database'], '-f', db_file_name)
end
end
......@@ -45,7 +45,7 @@ module Backup
'encoding' => '--default-character-set',
'password' => '--password'
}
args.map { |opt, arg| "#{arg}='#{config[opt]}'" if config[opt] }.compact.join(' ')
args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
end
def pg_env
......
module Backup
class Manager
BACKUP_CONTENTS = %w{repositories/ db/ uploads/ backup_information.yml}
def pack
# saving additional informations
s = {}
......@@ -16,7 +18,7 @@ module Backup
# create archive
print "Creating backup archive: #{s[:backup_created_at].to_i}_gitlab_backup.tar ... "
if Kernel.system("tar -cf #{s[:backup_created_at].to_i}_gitlab_backup.tar repositories/ db/ uploads/ backup_information.yml")
if Kernel.system('tar', '-cf', "#{s[:backup_created_at].to_i}_gitlab_backup.tar", *BACKUP_CONTENTS)
puts "done".green
else
puts "failed".red
......@@ -25,7 +27,7 @@ module Backup
def cleanup
print "Deleting tmp directories ... "
if Kernel.system("rm -rf repositories/ db/ uploads/ backup_information.yml")
if Kernel.system('rm', '-rf', *BACKUP_CONTENTS)
puts "done".green
else
puts "failed".red
......@@ -44,7 +46,7 @@ module Backup
file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
file_list.sort.each do |timestamp|
if Time.at(timestamp) < (Time.now - keep_time)
if system("rm #{timestamp}_gitlab_backup.tar")
if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
removed += 1
end
end
......@@ -75,7 +77,7 @@ module Backup
end
print "Unpacking backup ... "
unless Kernel.system("tar -xf #{tar_file}")
unless Kernel.system(*%W(tar -xf #{tar_file}))
puts "failed".red
exit 1
else
......
......@@ -18,7 +18,7 @@ module Backup
# Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
if system("cd #{path_to_repo(project)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(project)} --all > /dev/null 2>&1")
if system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent)
puts "[DONE]".green
else
puts "[FAILED]".red
......@@ -30,7 +30,7 @@ module Backup
print " * #{wiki.path_with_namespace} ... "
if wiki.empty?
puts " [SKIPPED]".cyan
elsif system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1")
elsif system(*%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all), silent)
puts " [DONE]".green
else
puts " [FAILED]".red
......@@ -53,7 +53,7 @@ module Backup
project.namespace.ensure_dir_exist if project.namespace
if system("git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)} > /dev/null 2>&1")
if system(*%W(git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)}), silent)
puts "[DONE]".green
else
puts "[FAILED]".red
......@@ -63,7 +63,7 @@ module Backup
if File.exists?(path_to_bundle(wiki))
print " * #{wiki.path_with_namespace} ... "
if system("git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)} > /dev/null 2>&1")
if system(*%W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}), silent)
puts " [DONE]".green
else
puts " [FAILED]".red
......@@ -73,7 +73,7 @@ module Backup
print 'Put GitLab hooks in repositories dirs'.yellow
gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh #{Gitlab.config.gitlab_shell.repos_path}")
if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh", Gitlab.config.gitlab_shell.repos_path)
puts " [DONE]".green
else
puts " [FAILED]".red
......@@ -103,5 +103,9 @@ module Backup
FileUtils.rm_rf(backup_repos_path)
FileUtils.mkdir_p(backup_repos_path)
end
def silent
{err: '/dev/null', out: '/dev/null'}
end
end
end
......@@ -19,7 +19,7 @@ module Backup
FileUtils.cp_r(backup_uploads_dir, app_uploads_dir)
end
def backup_existing_uploads_dir
if File.exists?(app_uploads_dir)
FileUtils.mv(app_uploads_dir, Rails.root.join('public', "uploads.#{Time.now.to_i}"))
......
......@@ -38,6 +38,16 @@ module Grack
# Authentication with username and password
login, password = @auth.credentials
# Allow authentication for GitLab CI service
# if valid token passed
if login == "gitlab-ci-token" && project.gitlab_ci?
token = project.gitlab_ci_service.token
if token.present? && token == password && service_name == 'git-upload-pack'
return @app.call(env)
end
end
@user = authenticate_user(login, password)
if @user
......@@ -59,14 +69,7 @@ module Grack
end
def authorized_git_request?
# Git upload and receive
if @request.get?
authorize_request(@request.params['service'])
elsif @request.post?
authorize_request(File.basename(@request.path))
else
false
end
authorize_request(service_name)
end
def authenticate_user(login, password)
......@@ -79,27 +82,41 @@ module Grack
when 'git-upload-pack'
project.public || can?(user, :download_code, project)
when'git-receive-pack'
action = if project.protected_branch?(ref)
:push_code_to_protected_branches
else
:push_code
end
refs.each do |ref|
action = if project.protected_branch?(ref)
:push_code_to_protected_branches
else
:push_code
end
return false unless can?(user, action, project)
end
can?(user, action, project)
true
else
false
end
end
def service_name
if @request.get?
@request.params['service']
elsif @request.post?
File.basename(@request.path)
else
nil
end
end
def project
@project ||= project_by_path(@request.path_info)
end
def ref
@ref ||= parse_ref
def refs
@refs ||= parse_refs
end
def parse_ref
def parse_refs
input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
Zlib::GzipReader.new(@request.body).read
else
......@@ -108,7 +125,15 @@ module Grack
# Need to reset seek point
@request.body.rewind
/refs\/heads\/([\/\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
# Parse refs
refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
# Cleanup grabare from refs
# if push to multiple branches
refs.map do |ref|
ref.gsub(/00.*/, "")
end
end
end
end
......@@ -196,6 +196,15 @@ module Gitlab
Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git"
end
# Return GitLab shell version
def version
gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION"
if File.readable?(gitlab_shell_version_file)
File.read(gitlab_shell_version_file)
end
end
protected
def gitlab_shell_user_home
......
......@@ -18,10 +18,33 @@ module Gitlab
default_regex
end
def git_reference_regex
# Valid git ref regex, see:
# https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
%r{
(?!
# doesn't begins with
\/| # (rule #6)
# doesn't contain
.*(?:
[\/.]\.| # (rule #1,3)
\/\/| # (rule #6)
@\{| # (rule #8)
\\ # (rule #9)
)
)
[^\000-\040\177~^:?*\[]+ # (rule #4-5)
# doesn't end with
(?<!\.lock) # (rule #1)
(?<![\/.]) # (rule #6-7)
}x
end
protected
def default_regex
/\A[a-zA-Z0-9][a-zA-Z0-9_\-\.]*\z/
/\A[a-zA-Z0-9][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
end
end
end
require_relative 'file_action'
module Gitlab
module Satellite
class DeleteFileAction < FileAction
# Deletes file and creates a new commit for it
#
# Returns false if committing the change fails
# Returns false if pushing from the satellite to bare repo failed or was rejected
# Returns true otherwise
def commit!(content, commit_message)
in_locked_and_timed_satellite do |repo|
prepare_satellite!(repo)
# create target branch in satellite at the corresponding commit from bare repo
repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
# update the file in the satellite's working dir
file_path_in_satellite = File.join(repo.working_dir, file_path)
# Prevent relative links
unless safe_path?(file_path_in_satellite)
Gitlab::GitLogger.error("FileAction: Relative path not allowed")
return false
end
File.delete(file_path_in_satellite)
# add removed file
repo.remove(file_path_in_satellite)
# commit the changes
# will raise CommandFailed when commit fails
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
# push commit back to bare repo
# will raise CommandFailed when push fails
repo.git.push({raise: true, timeout: true}, :origin, ref)
# everything worked
true
end
rescue Grit::Git::CommandFailed => ex
Gitlab::GitLogger.error(ex.message)
false
end
end
end
end
require_relative 'file_action'
module Gitlab
module Satellite
# GitLab server-side file update and commit
class EditFileAction < Action
attr_accessor :file_path, :ref
def initialize(user, project, ref, file_path)
super user, project, git_timeout: 10.seconds
@file_path = file_path
@ref = ref
end
class EditFileAction < FileAction
# Updates the files content and creates a new commit for it
#
# Returns false if the ref has been updated while editing the file
# Returns false if committing the change fails
# Returns false if pushing from the satellite to Gitolite failed or was rejected
# Returns false if pushing from the satellite to bare repo failed or was rejected
# Returns true otherwise
def commit!(content, commit_message, last_commit)
return false unless can_edit?(last_commit)
def commit!(content, commit_message)
in_locked_and_timed_satellite do |repo|
prepare_satellite!(repo)
# create target branch in satellite at the corresponding commit from Gitolite
# create target branch in satellite at the corresponding commit from bare repo
repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
# update the file in the satellite's working dir
file_path_in_satellite = File.join(repo.working_dir, file_path)
# Prevent relative links
unless safe_path?(file_path_in_satellite)
Gitlab::GitLogger.error("FileAction: Relative path not allowed")
return false
end
File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
# commit the changes
......@@ -34,7 +33,7 @@ module Gitlab
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
# push commit back to Gitolite
# push commit back to bare repo
# will raise CommandFailed when push fails
repo.git.push({raise: true, timeout: true}, :origin, ref)
......@@ -45,13 +44,6 @@ module Gitlab
Gitlab::GitLogger.error(ex.message)
false
end
protected
def can_edit?(last_commit)
current_last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, ref, file_path).sha
last_commit == current_last_commit
end
end
end
end
module Gitlab
module Satellite
class FileAction < Action
attr_accessor :file_path, :ref
def initialize(user, project, ref, file_path)
super user, project, git_timeout: 10.seconds
@file_path = file_path
@ref = ref
end
def safe_path?(path)
File.absolute_path(path) == path
end
end
end
end
require_relative 'file_action'
module Gitlab
module Satellite
class NewFileAction < FileAction
# Updates the files content and creates a new commit for it
#
# Returns false if the ref has been updated while editing the file
# Returns false if committing the change fails
# Returns false if pushing from the satellite to bare repo failed or was rejected
# Returns true otherwise
def commit!(content, commit_message)
in_locked_and_timed_satellite do |repo|
prepare_satellite!(repo)
# create target branch in satellite at the corresponding commit from bare repo
repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
file_path_in_satellite = File.join(repo.working_dir, file_path)
dir_name_in_satellite = File.dirname(file_path_in_satellite)
# Prevent relative links
unless safe_path?(file_path_in_satellite)
Gitlab::GitLogger.error("FileAction: Relative path not allowed")
return false
end
# Create dir if not exists
FileUtils.mkdir_p(dir_name_in_satellite)
# Write file
File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
# add new file
repo.add(file_path_in_satellite)
# commit the changes
# will raise CommandFailed when commit fails
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
# push commit back to bare repo
# will raise CommandFailed when push fails
repo.git.push({raise: true, timeout: true}, :origin, ref)
# everything worked
true
end
rescue Grit::Git::CommandFailed => ex
Gitlab::GitLogger.error(ex.message)
false
end
end
end
end
......@@ -28,7 +28,7 @@ module Gitlab
in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
if merge_in_satellite!(merge_repo)
# push merge back to Gitolite
# push merge back to bare repo
# will raise CommandFailed when push fails
merge_repo.git.push(default_options, :origin, merge_request.target_branch)
# remove source branch
......
......@@ -123,7 +123,7 @@ module Gitlab
remotes.each { |name| repo.git.remote(default_options,'rm', name)}
end
# Updates the satellite from Gitolite
# Updates the satellite from bare repo
#
# Note: this will only update remote branches (i.e. origin/*)
def update_from_source!
......
......@@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
def preprocess(full_document)
if @project
h.create_relative_links(full_document, @project.path_with_namespace, @ref, @request_path, is_wiki?)
h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?)
else
full_document
end
......
......@@ -20,8 +20,8 @@ RAILS_ENV="production"
# Script variable names should be lower-case not to conflict with internal
# /bin/sh variables such as PATH, EDITOR or SHELL.
app_root="/home/git/gitlab"
app_user="git"
app_root="/home/$app_user/gitlab"
pid_path="$app_root/tmp/pids"
socket_path="$app_root/tmp/sockets"
web_server_pid_path="$pid_path/unicorn.pid"
......@@ -44,6 +44,7 @@ fi
### Init Script functions
## Gets the pids from the files
check_pids(){
if ! mkdir -p "$pid_path"; then
echo "Could not create the path $pid_path needed to store the pids."
......@@ -62,12 +63,29 @@ check_pids(){
fi
}
## Called when we have started the two processes and are waiting for their pid files.
wait_for_pids(){
# We are sleeping a bit here mostly because sidekiq is slow at writing it's pid
i=0;
while [ ! -f $web_server_pid_path -o ! -f $sidekiq_pid_path ]; do
sleep 0.1;
i=$((i+1))
if [ $((i%10)) = 0 ]; then
echo -n "."
elif [ $((i)) = 301 ]; then
echo "Waited 30s for the processes to write their pids, something probably went wrong."
exit 1;
fi
done
echo
}
# We use the pids in so many parts of the script it makes sense to always check them.
# Only after start() is run should the pids change. Sidekiq sets it's own pid.
check_pids
# Checks whether the different parts of the service are already running or not.
## Checks whether the different parts of the service are already running or not.
check_status(){
check_pids
# If the web server is running kill -0 $wpid returns true, or rather 0.
......@@ -84,9 +102,16 @@ check_status(){
else
sidekiq_status="-1"
fi
if [ $web_status = 0 -a $sidekiq_status = 0 ]; then
gitlab_status=0
else
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
# code 3 means 'program is not running'
gitlab_status=3
fi
}
# Check for stale pids and remove them if necessary
## Check for stale pids and remove them if necessary.
check_stale_pids(){
check_status
# If there is a pid it is something else than 0, the service is running if
......@@ -94,7 +119,7 @@ check_stale_pids(){
if [ "$wpid" != "0" -a "$web_status" != "0" ]; then
echo "Removing stale Unicorn web server pid. This is most likely caused by the web server crashing the last time it ran."
if ! rm "$web_server_pid_path"; then
echo "Unable to remove stale pid, exiting"
echo "Unable to remove stale pid, exiting."
exit 1
fi
fi
......@@ -107,7 +132,7 @@ check_stale_pids(){
fi
}
# If no parts of the service is running, bail out.
## If no parts of the service is running, bail out.
exit_if_not_running(){
check_stale_pids
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
......@@ -116,86 +141,92 @@ exit_if_not_running(){
fi
}
# Starts Unicorn and Sidekiq.
## Starts Unicorn and Sidekiq if they're not running.
start() {
check_stale_pids
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
echo -n "Starting both the GitLab Unicorn and Sidekiq"
elif [ "$web_status" != "0" ]; then
echo -n "Starting GitLab Sidekiq"
elif [ "$sidekiq_status" != "0" ]; then
echo -n "Starting GitLab Unicorn"
fi
# Then check if the service is running. If it is: don't start again.
if [ "$web_status" = "0" ]; then
echo "The Unicorn web server already running with pid $wpid, not restarting."
else
echo "Starting the GitLab Unicorn web server..."
# Remove old socket if it exists
rm -f "$socket_path"/gitlab.socket 2>/dev/null
# Start the webserver
RAILS_ENV=$RAILS_ENV script/web start
# Start the web server
RAILS_ENV=$RAILS_ENV script/web start &
fi
# If sidekiq is already running, don't start it again.
if [ "$sidekiq_status" = "0" ]; then
echo "The Sidekiq job dispatcher is already running with pid $spid, not restarting"
else
echo "Starting the GitLab Sidekiq event dispatcher..."
RAILS_ENV=$RAILS_ENV script/background_jobs start
# We are sleeping a bit here because sidekiq is slow at writing it's pid
sleep 2
RAILS_ENV=$RAILS_ENV script/background_jobs start &
fi
# Wait for the pids to be planted
wait_for_pids
# Finally check the status to tell wether or not GitLab is running
status
print_status
}
# Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them.
## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them.
stop() {
exit_if_not_running
if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
echo -n "Shutting down both Unicorn and Sidekiq"
elif [ "$web_status" = "0" ]; then
echo -n "Shutting down Sidekiq"
elif [ "$sidekiq_status" = "0" ]; then
echo -n "Shutting down Unicorn"
fi
# If the Unicorn web server is running, tell it to stop;
if [ "$web_status" = "0" ]; then
RAILS_ENV=$RAILS_ENV script/web stop
echo "Stopping the GitLab Unicorn web server..."
stopping=true
else
echo "The Unicorn web was not running, doing nothing."
RAILS_ENV=$RAILS_ENV script/web stop
fi
# And do the same thing for the Sidekiq.
if [ "$sidekiq_status" = "0" ]; then
printf "Stopping Sidekiq job dispatcher."
RAILS_ENV=$RAILS_ENV script/background_jobs stop
stopping=true
else
echo "The Sidekiq was not running, must have run out of breath."
fi
# If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script.
while [ "$stopping" = "true" ]; do
while [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; do
sleep 1
check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
printf "."
else
printf "."
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
printf "\n"
break
fi
done
sleep 1
# Cleaning up unused pids
rm "$web_server_pid_path" 2>/dev/null
# rm "$sidekiq_pid_path" # Sidekiq seems to be cleaning up it's own pid.
status
print_status
}
# Returns the status of GitLab and it's components
status() {
## Prints the status of GitLab and it's components.
print_status() {
check_status
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
echo "GitLab is not running."
return
fi
if [ "$web_status" = "0" ]; then
echo "The GitLab Unicorn webserver with pid $wpid is running."
echo "The GitLab Unicorn web server with pid $wpid is running."
else
printf "The GitLab Unicorn webserver is \033[31mnot running\033[0m.\n"
printf "The GitLab Unicorn web server is \033[31mnot running\033[0m.\n"
fi
if [ "$sidekiq_status" = "0" ]; then
echo "The GitLab Sidekiq job dispatcher with pid $spid is running."
......@@ -207,6 +238,7 @@ status() {
fi
}
## Tells unicorn to reload it's config and Sidekiq to restart
reload(){
exit_if_not_running
if [ "$wpid" = "0" ];then
......@@ -218,11 +250,12 @@ reload(){
echo "Done."
echo "Restarting GitLab Sidekiq since it isn't capable of reloading its config..."
RAILS_ENV=$RAILS_ENV script/background_jobs restart
# Waiting 2 seconds for sidekiq to write it.
sleep 2
status
wait_for_pids
print_status
}
## Restarts Sidekiq and Unicorn.
restart(){
check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
......@@ -232,7 +265,7 @@ restart(){
}
## Finally the input handling.
### Finally the input handling.
case "$1" in
start)
......@@ -248,7 +281,8 @@ case "$1" in
reload
;;
status)
status
print_status
exit $gitlab_status
;;
*)
echo "Usage: service gitlab {start|stop|restart|reload|status}"
......
......@@ -611,10 +611,7 @@ namespace :gitlab do
end
def gitlab_shell_version
gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION"
if File.readable?(gitlab_shell_version_file)
File.read(gitlab_shell_version_file)
end
Gitlab::Shell.new.version
end
def has_gitlab_shell3?
......@@ -648,7 +645,7 @@ namespace :gitlab do
else
puts "no".red
try_fixing_it(
sudo_gitlab("bundle exec rake sidekiq:start RAILS_ENV=production")
sudo_gitlab("RAILS_ENV=production script/background_jobs start")
)
for_more_information(
see_installation_guide_section("Install Init Script"),
......@@ -736,7 +733,7 @@ namespace :gitlab do
end
def check_gitlab_shell
required_version = Gitlab::VersionInfo.new(1, 7, 4)
required_version = Gitlab::VersionInfo.new(1, 7, 9)
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_version} ? ... "
......
require 'spec_helper'
describe SearchContext do
let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
let(:user) { create(:user, namespace: found_namespace) }
let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, public: false) }
let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, public: false) }
let(:public_namespace) { create(:namespace, path: 'something_else',name: 'searchable public namespace') }
let(:other_user) { create(:user, namespace: public_namespace) }
let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: other_user.id, namespace: public_namespace, public: true) }
describe '#execute' do
it 'public projects should be searchable' do
context = SearchContext.new([found_project.id], {search_code: false, search: "searchable"})
results = context.execute
results[:projects].should == [found_project, public_project]
end
it 'namespace name should be searchable' do
context = SearchContext.new([found_project.id], {search_code: false, search: "searchable namespace"})
results = context.execute
results[:projects].should == [found_project]
end
end
end
......@@ -66,6 +66,7 @@ FactoryGirl.define do
after :create do |project|
TestEnv.clear_repo_dir(project.namespace, project.path)
TestEnv.reset_satellite_dir
TestEnv.create_repo(project.namespace, project.path)
end
end
......
# == Schema Information
#
# Table name: broadcast_messages
#
# id :integer not null, primary key
# message :text default(""), not null
# starts_at :datetime
# ends_at :datetime
# alert_type :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :broadcast_message do
message "MyText"
starts_at "2013-11-12 13:43:25"
ends_at "2013-11-12 13:43:25"
alert_type 1
end
end
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the NotificationsHelper. For example:
#
# describe NotificationsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe NotificationsHelper do
pending "add some examples to (or delete) #{__FILE__}"
describe 'notification_icon' do
let(:notification) { stub(disabled?: false, participating?: false, watch?: false) }
context "disabled notification" do
before { notification.stub(disabled?: true) }
it "has a red icon" do
notification_icon(notification).should match('class="icon-circle cred"')
end
end
context "participating notification" do
before { notification.stub(participating?: true) }
it "has a blue icon" do
notification_icon(notification).should match('class="icon-circle cblue"')
end
end
context "watched notification" do
before { notification.stub(watch?: true) }
it "has a green icon" do
notification_icon(notification).should match('class="icon-circle cgreen"')
end
end
it "has a blue icon" do
notification_icon(notification).should match('class="icon-circle-blank cblue"')
end
end
end
......@@ -7,5 +7,17 @@ describe ProjectsHelper do
"<option value=\"redmine\">Redmine</option>\n" \
"<option value=\"gitlab\">GitLab</option>"
end
it "returns the correct issues trackers available with current tracker 'gitlab' selected" do
project_issues_trackers('gitlab').should ==
"<option value=\"redmine\">Redmine</option>\n" \
"<option value=\"gitlab\" selected=\"selected\">GitLab</option>"
end
it "returns the correct issues trackers available with current tracker 'redmine' selected" do
project_issues_trackers('redmine').should ==
"<option value=\"redmine\" selected=\"selected\">Redmine</option>\n" \
"<option value=\"gitlab\">GitLab</option>"
end
end
end
......@@ -110,7 +110,7 @@ describe Notify do
it_behaves_like 'an assignee email'
it 'has the correct subject' do
should have_subject /#{project.name} \| new issue ##{issue.iid} \| #{issue.title}/
should have_subject /#{project.name} \| New issue ##{issue.iid} \| #{issue.title}/
end
it 'contains a link to the new issue' do
......@@ -126,7 +126,7 @@ describe Notify do
it_behaves_like 'a multiple recipients email'
it 'has the correct subject' do
should have_subject /changed issue ##{issue.iid} \| #{issue.title}/
should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/
end
it 'contains the name of the previous assignee' do
......@@ -148,7 +148,7 @@ describe Notify do
subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) }
it 'has the correct subject' do
should have_subject /changed issue ##{issue.iid} \| #{issue.title}/i
should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/i
end
it 'contains the new status' do
......@@ -175,7 +175,7 @@ describe Notify do
it_behaves_like 'an assignee email'
it 'has the correct subject' do
should have_subject /new merge request !#{merge_request.iid}/
should have_subject /New merge request ##{merge_request.iid}/
end
it 'contains a link to the new merge request' do
......@@ -199,7 +199,7 @@ describe Notify do
it_behaves_like 'a multiple recipients email'
it 'has the correct subject' do
should have_subject /changed merge request !#{merge_request.iid}/
should have_subject /Changed merge request ##{merge_request.iid}/
end
it 'contains the name of the previous assignee' do
......@@ -224,7 +224,7 @@ describe Notify do
subject { Notify.project_was_moved_email(project.id, user.id) }
it 'has the correct subject' do
should have_subject /project was moved/
should have_subject /Project was moved/
end
it 'contains name of project' do
......@@ -244,7 +244,7 @@ describe Notify do
user: user) }
subject { Notify.project_access_granted_email(users_project.id) }
it 'has the correct subject' do
should have_subject /access to project was granted/
should have_subject /Access to project was granted/
end
it 'contains name of project' do
should have_body_text /#{project.name}/
......@@ -302,7 +302,7 @@ describe Notify do
it_behaves_like 'a note email'
it 'has the correct subject' do
should have_subject /note for commit #{commit.short_id}/
should have_subject /Note for commit #{commit.short_id}/
end
it 'contains a link to the commit' do
......@@ -320,7 +320,7 @@ describe Notify do
it_behaves_like 'a note email'
it 'has the correct subject' do
should have_subject /note for merge request ##{merge_request.iid}/
should have_subject /Note for merge request ##{merge_request.iid}/
end
it 'contains a link to the merge request note' do
......@@ -338,7 +338,7 @@ describe Notify do
it_behaves_like 'a note email'
it 'has the correct subject' do
should have_subject /note for issue ##{issue.iid}/
should have_subject /Note for issue ##{issue.iid}/
end
it 'contains a link to the issue note' do
......@@ -356,7 +356,7 @@ describe Notify do
subject { Notify.group_access_granted_email(membership.id) }
it 'has the correct subject' do
should have_subject /access to group was granted/
should have_subject /Access to group was granted/
end
it 'contains name of project' do
......@@ -367,4 +367,28 @@ describe Notify do
should have_body_text /#{membership.human_access}/
end
end
describe 'confirmation if email changed' do
let(:example_site_path) { root_path }
let(:user) { create(:user, email: 'old-email@mail.com') }
before do
user.email = "new-email@mail.com"
user.save
end
subject { ActionMailer::Base.deliveries.last }
it 'is sent to the new user' do
should deliver_to 'new-email@mail.com'
end
it 'has the correct subject' do
should have_subject "Confirmation instructions"
end
it 'includes a link to the site' do
should have_body_text /#{example_site_path}/
end
end
end
# == Schema Information
#
# Table name: broadcast_messages
#
# id :integer not null, primary key
# message :text default(""), not null
# starts_at :datetime
# ends_at :datetime
# alert_type :integer
# created_at :datetime not null
# updated_at :datetime not null
#
require 'spec_helper'
describe BroadcastMessage do
subject { create(:broadcast_message) }
it { should be_valid }
describe :current do
it "should return last message if time match" do
broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow)
BroadcastMessage.current.should == broadcast_message
end
it "should return nil if time not come" do
broadcast_message = create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days)
BroadcastMessage.current.should be_nil
end
it "should return nil if time has passed" do
broadcast_message = create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday)
BroadcastMessage.current.should be_nil
end
end
end
......@@ -11,6 +11,8 @@
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
#
require 'spec_helper'
......
......@@ -116,13 +116,13 @@ describe MergeRequest do
end
it 'accesses the set of issues that will be closed on acceptance' do
subject.project.default_branch = subject.target_branch
subject.project.stub(default_branch: subject.target_branch)
subject.closes_issues.should == [issue0, issue1].sort_by(&:id)
end
it 'only lists issues as to be closed if it targets the default branch' do
subject.project.default_branch = 'master'
subject.project.stub(default_branch: 'master')
subject.target_branch = 'something-else'
subject.closes_issues.should be_empty
......
......@@ -9,7 +9,6 @@
# created_at :datetime not null
# updated_at :datetime not null
# creator_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
......
......@@ -36,6 +36,11 @@
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
# avatar :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
#
require 'spec_helper'
......@@ -135,7 +140,6 @@ describe User do
end
it { @user.several_namespaces?.should be_true }
it { @user.namespaces.should include(@user.namespace) }
it { @user.authorized_groups.should == [@group] }
it { @user.owned_groups.should == [@group] }
end
......@@ -162,7 +166,6 @@ describe User do
end
it { @user.several_namespaces?.should be_false }
it { @user.namespaces.should == [@user.namespace] }
end
describe 'blocking user' do
......
require 'spec_helper'
describe API::API do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let!(:project) { create(:project_with_code, namespace: user.namespace ) }
before { project.team << [user, :developer] }
describe "POST /projects/:id/repository/files" do
let(:valid_params) {
{
file_path: 'newfile.rb',
branch_name: 'master',
content: 'puts 8',
commit_message: 'Added newfile'
}
}
it "should create a new file in project repo" do
Gitlab::Satellite::NewFileAction.any_instance.stub(
commit!: true,
)
post api("/projects/#{project.id}/repository/files", user), valid_params
response.status.should == 201
json_response['file_path'].should == 'newfile.rb'
end
it "should return a 400 bad request if no params given" do
post api("/projects/#{project.id}/repository/files", user)
response.status.should == 400
end
it "should return a 400 if satellite fails to create file" do
Gitlab::Satellite::NewFileAction.any_instance.stub(
commit!: false,
)
post api("/projects/#{project.id}/repository/files", user), valid_params
response.status.should == 400
end
end
describe "PUT /projects/:id/repository/files" do
let(:valid_params) {
{
file_path: 'spec/spec_helper.rb',
branch_name: 'master',
content: 'puts 8',
commit_message: 'Changed file'
}
}
it "should update existing file in project repo" do
Gitlab::Satellite::EditFileAction.any_instance.stub(
commit!: true,
)
put api("/projects/#{project.id}/repository/files", user), valid_params
response.status.should == 200
json_response['file_path'].should == 'spec/spec_helper.rb'
end
it "should return a 400 bad request if no params given" do
put api("/projects/#{project.id}/repository/files", user)
response.status.should == 400
end
it "should return a 400 if satellite fails to create file" do
Gitlab::Satellite::EditFileAction.any_instance.stub(
commit!: false,
)
put api("/projects/#{project.id}/repository/files", user), valid_params
response.status.should == 400
end
end
describe "DELETE /projects/:id/repository/files" do
let(:valid_params) {
{
file_path: 'spec/spec_helper.rb',
branch_name: 'master',
commit_message: 'Changed file'
}
}
it "should delete existing file in project repo" do
Gitlab::Satellite::DeleteFileAction.any_instance.stub(
commit!: true,
)
delete api("/projects/#{project.id}/repository/files", user), valid_params
response.status.should == 200
json_response['file_path'].should == 'spec/spec_helper.rb'
end
it "should return a 400 bad request if no params given" do
delete api("/projects/#{project.id}/repository/files", user)
response.status.should == 400
end
it "should return a 400 if satellite fails to create file" do
Gitlab::Satellite::DeleteFileAction.any_instance.stub(
commit!: false,
)
delete api("/projects/#{project.id}/repository/files", user), valid_params
response.status.should == 400
end
end
end
require 'spec_helper'
describe API::API do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:admin) { create(:admin) }
let!(:group1) { create(:group) }
let!(:group2) { create(:group) }
describe "GET /namespaces" do
context "when unauthenticated" do
it "should return authentication error" do
get api("/namespaces")
response.status.should == 401
end
end
context "when authenticated as admin" do
it "admin: should return an array of all namespaces" do
get api("/namespaces", admin)
response.status.should == 200
json_response.should be_an Array
# Admin namespace + 2 group namespaces
json_response.length.should == 3
end
end
end
end
......@@ -36,6 +36,32 @@ describe API::API do
end
end
describe "GET /projects/all" do
context "when unauthenticated" do
it "should return authentication error" do
get api("/projects/all")
response.status.should == 401
end
end
context "when authenticated as regular user" do
it "should return authentication error" do
get api("/projects/all", user)
response.status.should == 403
end
end
context "when authenticated as admin" do
it "should return an array of all projects" do
get api("/projects/all", admin)
response.status.should == 200
json_response.should be_an Array
json_response.first['name'].should == project.name
json_response.first['owner']['email'].should == user.email
end
end
end
describe "POST /projects" do
context "maximum number of projects reached" do
before do
......@@ -91,7 +117,6 @@ describe API::API do
it "should assign attributes to project" do
project = attributes_for(:project, {
description: Faker::Lorem.sentence,
default_branch: 'stable',
issues_enabled: false,
wall_enabled: false,
merge_requests_enabled: false,
......@@ -110,16 +135,13 @@ describe API::API do
project = attributes_for(:project, { public: true })
post api("/projects", user), project
json_response['public'].should be_true
end
it "should set a project as private" do
project = attributes_for(:project, { public: false })
post api("/projects", user), project
json_response['public'].should be_false
end
end
describe "POST /projects/user/:id" do
......@@ -146,7 +168,6 @@ describe API::API do
it "should assign attributes to project" do
project = attributes_for(:project, {
description: Faker::Lorem.sentence,
default_branch: 'stable',
issues_enabled: false,
wall_enabled: false,
merge_requests_enabled: false,
......
require "spec_helper"
describe API::API do
include ApiHelpers
before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
let(:user) { create(:user) }
let(:project) {create(:project_with_code, creator_id: user.id, namespace: user.namespace) }
describe "POST /projects/:id/services/gitlab-ci" do
it "should update gitlab-ci settings" do
put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'secret-token', project_url: "http://ci.example.com/projects/1"
response.status.should == 200
end
it "should return if required fields missing" do
put api("/projects/#{project.id}/services/gitlab-ci", user), project_url: "http://ci.example.com/projects/1", active: true
response.status.should == 400
end
end
describe "DELETE /projects/:id/services/gitlab-ci" do
it "should update gitlab-ci settings" do
delete api("/projects/#{project.id}/services/gitlab-ci", user)
response.status.should == 200
project.gitlab_ci_service.should be_nil
end
end
end
......@@ -138,12 +138,24 @@ end
describe Projects::BranchesController, "routing" do
it "to #branches" do
get("/gitlab/gitlabhq/branches").should route_to('projects/branches#index', project_id: 'gitlab/gitlabhq')
delete("/gitlab/gitlabhq/branches/feature%2345").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
delete("/gitlab/gitlabhq/branches/feature%2B45").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
delete("/gitlab/gitlabhq/branches/feature@45").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
delete("/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
delete("/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
delete("/gitlab/gitlabhq/branches/feature@45/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
end
end
describe Projects::TagsController, "routing" do
it "to #tags" do
get("/gitlab/gitlabhq/tags").should route_to('projects/tags#index', project_id: 'gitlab/gitlabhq')
delete("/gitlab/gitlabhq/tags/feature%2345").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
delete("/gitlab/gitlabhq/tags/feature%2B45").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
delete("/gitlab/gitlabhq/tags/feature@45").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
delete("/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
delete("/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
delete("/gitlab/gitlabhq/tags/feature@45/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
end
end
......@@ -183,9 +195,11 @@ describe Projects::RefsController, "routing" do
get("/gitlab/gitlabhq/refs/stable/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable')
get("/gitlab/gitlabhq/refs/feature%2345/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45')
get("/gitlab/gitlabhq/refs/feature%2B45/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45')
get("/gitlab/gitlabhq/refs/feature@45/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45')
get("/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'foo/bar/baz')
get("/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
get("/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
get("/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45', path: 'foo/bar/baz')
get("/gitlab/gitlabhq/refs/stable/logs_tree/files.scss").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss')
end
end
......
......@@ -45,6 +45,7 @@ module TestEnv
def disable_mailer
NotificationService.any_instance.stub(mailer: double.as_null_object)
end
def enable_mailer
NotificationService.any_instance.unstub(:mailer)
end
......@@ -68,7 +69,8 @@ module TestEnv
remove_repository: true,
update_repository_head: true,
add_key: true,
remove_key: true
remove_key: true,
version: '6.3.0'
)
Gitlab::Satellite::Satellite.any_instance.stub(
......@@ -96,6 +98,15 @@ module TestEnv
FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git")
end
def reset_satellite_dir
setup_stubs
FileUtils.cd(seed_satellite_path) do
`git reset --hard --quiet`
`git clean -fx`
`git checkout --quiet origin/master`
end
end
# Create a repo and it's satellite
def create_repo(namespace, name)
setup_stubs
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment