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 language: ruby
env: env:
- DB=mysql TRAVIS=true global:
- DB=mysql
- TRAVIS=true
matrix:
- TASK=spinach
- TASK=spec
- TASK=jasmine:ci
before_install: before_install:
- sudo apt-get install libicu-dev -y - sudo apt-get install libicu-dev -y
- gem install charlock_holmes -v="0.6.9"
branches: branches:
only: only:
- 'master' - 'master'
...@@ -11,8 +16,9 @@ rvm: ...@@ -11,8 +16,9 @@ rvm:
- 2.0.0 - 2.0.0
services: services:
- mysql - mysql
- postgresql
before_script: before_script:
- "cp config/database.yml.$DB config/database.yml" - "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml" - "cp config/gitlab.yml.example config/gitlab.yml"
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 v 6.2.0
- Public project pages are now visible to everyone (files, issues, wik, etc.) - 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 THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
...@@ -15,7 +55,7 @@ v 6.2.0 ...@@ -15,7 +55,7 @@ v 6.2.0
- Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov) - Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
- API: Remove group - API: Remove group
- API: Remove project - 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 - Store the sessions in Redis instead of the cookie store
- Fixed relative links in markdown - Fixed relative links in markdown
- User must confirm his email if signup enabled - User must confirm his email if signup enabled
...@@ -83,6 +123,14 @@ v 6.0.0 ...@@ -83,6 +123,14 @@ v 6.0.0
- Improved MR comments logic - Improved MR comments logic
- Render readme file for projects in public area - 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 v 5.4.0
- Ability to edit own comments - Ability to edit own comments
- Documentation improvements - Documentation improvements
......
...@@ -5,9 +5,18 @@ This guide details how to use issues and pull requests to improve GitLab. ...@@ -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) - [Closing policy for issues and pull requests](#closing-policy-for-issues-and-pull-requests)
- [Issue tracker](#issue-tracker) - [Issue tracker](#issue-tracker)
- [Pull requests](#pull-requests) - [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). 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 ## 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. 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) ...@@ -8,7 +8,7 @@ def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as RUBY_PLATFORM.include?('linux') && require_as
end end
gem "rails", "3.2.13" gem "rails", "3.2.15"
# Supported DBs # Supported DBs
gem "mysql2", group: :mysql gem "mysql2", group: :mysql
......
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (3.2.13) actionmailer (3.2.15)
actionpack (= 3.2.13) actionpack (= 3.2.15)
mail (~> 2.5.3) mail (~> 2.5.4)
actionpack (3.2.13) actionpack (3.2.15)
activemodel (= 3.2.13) activemodel (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
builder (~> 3.0.0) builder (~> 3.0.0)
erubis (~> 2.7.0) erubis (~> 2.7.0)
journey (~> 1.0.4) journey (~> 1.0.4)
...@@ -14,19 +14,19 @@ GEM ...@@ -14,19 +14,19 @@ GEM
rack-cache (~> 1.2) rack-cache (~> 1.2)
rack-test (~> 0.6.1) rack-test (~> 0.6.1)
sprockets (~> 2.2.1) sprockets (~> 2.2.1)
activemodel (3.2.13) activemodel (3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
builder (~> 3.0.0) builder (~> 3.0.0)
activerecord (3.2.13) activerecord (3.2.15)
activemodel (= 3.2.13) activemodel (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
arel (~> 3.0.2) arel (~> 3.0.2)
tzinfo (~> 0.3.29) tzinfo (~> 0.3.29)
activeresource (3.2.13) activeresource (3.2.15)
activemodel (= 3.2.13) activemodel (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
activesupport (3.2.13) activesupport (3.2.15)
i18n (= 0.6.1) i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0) multi_json (~> 1.0)
acts-as-taggable-on (2.4.1) acts-as-taggable-on (2.4.1)
rails (>= 3, < 5) rails (>= 3, < 5)
...@@ -34,7 +34,7 @@ GEM ...@@ -34,7 +34,7 @@ GEM
annotate (2.6.0.beta2) annotate (2.6.0.beta2)
activerecord (>= 2.3.0) activerecord (>= 2.3.0)
rake (>= 0.8.7) rake (>= 0.8.7)
arel (3.0.2) arel (3.0.3)
asciidoctor (0.1.3) asciidoctor (0.1.3)
awesome_print (1.2.0) awesome_print (1.2.0)
backports (3.3.2) backports (3.3.2)
...@@ -108,7 +108,7 @@ GEM ...@@ -108,7 +108,7 @@ GEM
warden (~> 1.2.1) warden (~> 1.2.1)
devise-async (0.8.0) devise-async (0.8.0)
devise (>= 2.2, < 3.2) devise (>= 2.2, < 3.2)
diff-lcs (1.2.4) diff-lcs (1.2.5)
dotenv (0.8.0) dotenv (0.8.0)
email_spec (1.4.0) email_spec (1.4.0)
launchy (~> 2.1) launchy (~> 2.1)
...@@ -171,7 +171,7 @@ GEM ...@@ -171,7 +171,7 @@ GEM
stringex (~> 1.5.1) stringex (~> 1.5.1)
gitlab-grack (1.0.1) gitlab-grack (1.0.1)
rack (~> 1.4.1) rack (~> 1.4.1)
gitlab-grit (2.6.1) gitlab-grit (2.6.3)
charlock_holmes (~> 0.6.9) charlock_holmes (~> 0.6.9)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
...@@ -235,7 +235,7 @@ GEM ...@@ -235,7 +235,7 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpauth (0.2.0) httpauth (0.2.0)
i18n (0.6.1) i18n (0.6.5)
jasmine (1.3.2) jasmine (1.3.2)
jasmine-core (~> 1.3.1) jasmine-core (~> 1.3.1)
rack (~> 1.0) rack (~> 1.0)
...@@ -274,11 +274,11 @@ GEM ...@@ -274,11 +274,11 @@ GEM
mime-types (~> 1.16) mime-types (~> 1.16)
treetop (~> 1.4.8) treetop (~> 1.4.8)
method_source (0.8.1) method_source (0.8.1)
mime-types (1.25) mime-types (1.25.1)
minitest (4.7.4) minitest (4.7.4)
modernizr (2.6.2) modernizr (2.6.2)
sprockets (~> 2.0) sprockets (~> 2.0)
multi_json (1.8.0) multi_json (1.8.2)
multi_xml (0.5.4) multi_xml (0.5.4)
multipart-post (1.2.0) multipart-post (1.2.0)
mysql2 (0.3.11) mysql2 (0.3.11)
...@@ -348,14 +348,14 @@ GEM ...@@ -348,14 +348,14 @@ GEM
rack rack
rack-test (0.6.2) rack-test (0.6.2)
rack (>= 1.0) rack (>= 1.0)
rails (3.2.13) rails (3.2.15)
actionmailer (= 3.2.13) actionmailer (= 3.2.15)
actionpack (= 3.2.13) actionpack (= 3.2.15)
activerecord (= 3.2.13) activerecord (= 3.2.15)
activeresource (= 3.2.13) activeresource (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.2.13) railties (= 3.2.15)
rails-dev-tweaks (0.6.1) rails-dev-tweaks (0.6.1)
actionpack (~> 3.1) actionpack (~> 3.1)
railties (~> 3.1) railties (~> 3.1)
...@@ -368,9 +368,9 @@ GEM ...@@ -368,9 +368,9 @@ GEM
i18n i18n
require_all require_all
ruby-progressbar ruby-progressbar
railties (3.2.13) railties (3.2.15)
actionpack (= 3.2.13) actionpack (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
rack-ssl (~> 1.3.2) rack-ssl (~> 1.3.2)
rake (>= 0.8.7) rake (>= 0.8.7)
rdoc (~> 3.4) rdoc (~> 3.4)
...@@ -514,7 +514,7 @@ GEM ...@@ -514,7 +514,7 @@ GEM
multi_json (~> 1.5) multi_json (~> 1.5)
twitter-stream (~> 0.1) twitter-stream (~> 0.1)
tins (0.11.0) tins (0.11.0)
treetop (1.4.14) treetop (1.4.15)
polyglot polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
turbolinks (1.2.0) turbolinks (1.2.0)
...@@ -523,7 +523,7 @@ GEM ...@@ -523,7 +523,7 @@ GEM
eventmachine (>= 0.12.8) eventmachine (>= 0.12.8)
http_parser.rb (~> 0.5.1) http_parser.rb (~> 0.5.1)
simple_oauth (~> 0.1.4) simple_oauth (~> 0.1.4)
tzinfo (0.3.37) tzinfo (0.3.38)
uglifier (2.1.1) uglifier (2.1.1)
execjs (>= 0.3.0) execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2) multi_json (~> 1.0, >= 1.0.2)
...@@ -615,7 +615,7 @@ DEPENDENCIES ...@@ -615,7 +615,7 @@ DEPENDENCIES
quiet_assets (~> 1.0.1) quiet_assets (~> 1.0.1)
rack-attack rack-attack
rack-mini-profiler rack-mini-profiler
rails (= 3.2.13) rails (= 3.2.15)
rails-dev-tweaks rails-dev-tweaks
rails_best_practices rails_best_practices
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
......
...@@ -64,9 +64,10 @@ If you want to contribute, please first read our [Contributing Guidelines](https ...@@ -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. * [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 ### New versions and upgrading
...@@ -99,7 +100,7 @@ Start it with [Foreman](https://github.com/ddollar/foreman) ...@@ -99,7 +100,7 @@ Start it with [Foreman](https://github.com/ddollar/foreman)
or start each component separately or start each component separately
bundle exec rails s bundle exec rails s
bundle exec rake sidekiq:start script/background_jobs start
### Run the tests ### Run the tests
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
user_path: "/api/:version/users/:id.json" user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json" notes_path: "/api/:version/projects/:id/notes.json"
ldap_groups_path: "/api/:version/ldap/groups.json" ldap_groups_path: "/api/:version/ldap/groups.json"
namespaces_path: "/api/:version/namespaces.json"
# Get 20 (depends on api) recent notes # Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest # and sort the ascending from oldest to newest
...@@ -50,6 +51,20 @@ ...@@ -50,6 +51,20 @@
).done (users) -> ).done (users) ->
callback(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) -> buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root? url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version) return url.replace(':version', gon.api_version)
......
...@@ -123,6 +123,10 @@ $ -> ...@@ -123,6 +123,10 @@ $ ->
$(@).next('table').show() $(@).next('table').show()
$(@).remove() $(@).remove()
$(".content").on "click", ".js-details-expand", ->
$(@).next('.js-details-contain').removeClass("hide")
$(@).remove()
(($) -> (($) ->
_chosen = $.fn.chosen _chosen = $.fn.chosen
$.fn.extend chosen: (options) -> $.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 = { ...@@ -233,10 +233,12 @@ var NoteList = {
form.show(); form.show();
var textarea = form.find("textarea"); var textarea = form.find("textarea");
if (form.find(".note-original-content").length === 0) {
var p = $("<p></p>").text(textarea.val()); var p = $("<p></p>").text(textarea.val());
var hidden_div = $('<div class="note-original-content"></div>').append(p); var hidden_div = $('<div class="note-original-content"></div>').append(p);
form.append(hidden_div); form.append(hidden_div);
hidden_div.hide(); hidden_div.hide();
}
textarea.focus(); textarea.focus();
}, },
...@@ -532,6 +534,8 @@ var NoteList = { ...@@ -532,6 +534,8 @@ var NoteList = {
note_text.html(response.note).show(); note_text.html(response.note).show();
var note_form = note_li.find(".note-edit-form"); 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.hide();
note_form.find(".btn-save").enableButton(); note_form.find(".btn-save").enableButton();
......
...@@ -29,7 +29,7 @@ class Project ...@@ -29,7 +29,7 @@ class Project
$ -> $ ->
# Git clone panel switcher # Git clone panel switcher
scope = $ '.project_clone_holder' scope = $ '.git-clone-holder'
if scope.length > 0 if scope.length > 0
$('a, button', scope).click -> $('a, button', scope).click ->
$('a, button', scope).removeClass 'active' $('a, button', scope).removeClass 'active'
...@@ -40,3 +40,9 @@ $ -> ...@@ -40,3 +40,9 @@ $ ->
# Ref switcher # Ref switcher
$('.project-refs-select').on 'change', -> $('.project-refs-select').on 'change', ->
$(@).parents('form').submit() $(@).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 { ...@@ -86,13 +86,6 @@ span.update-author {
font-weight: bold; font-weight: bold;
} }
.label {
padding: 1px 4px;
font-size: 12px;
font-style: normal;
font-weight: normal;
}
.field_with_errors { .field_with_errors {
display: inline; display: inline;
} }
...@@ -136,6 +129,10 @@ p.time { ...@@ -136,6 +129,10 @@ p.time {
} }
} }
.highlight {
text-shadow: none;
}
.highlight_word { .highlight_word {
border-bottom: 2px solid #F90; border-bottom: 2px solid #F90;
} }
...@@ -223,7 +220,6 @@ li.note { ...@@ -223,7 +220,6 @@ li.note {
.error-message { .error-message {
padding: 10px; padding: 10px;
background: #C67; background: #C67;
padding-left: 20px;
margin: 0; margin: 0;
color: #FFF; color: #FFF;
...@@ -231,8 +227,18 @@ li.note { ...@@ -231,8 +227,18 @@ li.note {
color: #fff; color: #fff;
text-decoration: underline; text-decoration: underline;
} }
&.centered { }
.no-ssh-key-message {
padding: 10px 0;
background: #C67;
margin: 0;
color: #FFF;
text-align: center; text-align: center;
a {
color: #fff;
text-decoration: underline;
} }
} }
...@@ -349,3 +355,40 @@ table { ...@@ -349,3 +355,40 @@ table {
@extend .btn-new; @extend .btn-new;
padding: 5px 15px; 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 @@ ...@@ -2,8 +2,8 @@
float: left; float: left;
margin-right: 12px; margin-right: 12px;
width: 40px; width: 40px;
border: 1px solid #ddd;
padding: 1px; padding: 1px;
@include border-radius(4px);
&.avatar-inline { &.avatar-inline {
float: none; float: none;
......
...@@ -32,12 +32,9 @@ ...@@ -32,12 +32,9 @@
} }
&.ui-box-show { &.ui-box-show {
text-shadow: 0 1px 1px #fff;
color: #666; color: #666;
margin:20px 0; margin:20px 0;
background: #FFF; background: #FAFAFA;
box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1;
@include linear-gradient(#fafafa, #f1f1f1);
.control-group { .control-group {
margin-bottom: 0; margin-bottom: 0;
...@@ -45,11 +42,13 @@ ...@@ -45,11 +42,13 @@
} }
&.ui-box-danger { &.ui-box-danger {
background: #f7f7f7;
border: none;
.title { .title {
@include linear-gradient(#F26E5E, #bd362f); background: #D65;
color: #fff; color: #fff;
text-shadow: 0 1px 1px #900; text-shadow: 0 1px 1px #900;
font-weight: bold;
} }
} }
...@@ -99,9 +98,9 @@ ...@@ -99,9 +98,9 @@
} }
.title { .title {
@include bg-gray-gradient; background-color: #EEE;
border-bottom: 1px solid #CCC; border-bottom: 1px solid #DDD;
color: #456; color: #666;
font-size: 16px; font-size: 16px;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
padding: 0 10px; padding: 0 10px;
......
.btn { .btn {
display: inline-block; display: inline-block;
padding: 6px 12px;
margin-bottom: 0; margin-bottom: 0;
font-size: 13px; font-weight: normal;
line-height: $baseLineHeight;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
cursor: pointer; cursor: pointer;
border: 1px solid #BBB; background-image: none;
color: $style_color; border: 1px solid transparent;
@include border-radius($baseBorderRadius); white-space: nowrap;
@include box-shadow(inset 0 1px 0 rgba(255,255,255,.2)); padding: 6px 12px;
@include linear-gradient(#f1f1f1, #e1e1e1); font-size: 13px;
text-shadow: 0 1px 1px #FFF; line-height: 18px;
text-decoration: none; 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,
&:hover { &:hover {
color: $style_color; color: #444444;
background: #f1f1f1;
border-color: #AAA;
text-decoration: none; text-decoration: none;
@include linear-gradient(#fAfAfA, #f1f1f1); background-color: #ebebeb;
border-color: #adadad;
} }
&.focus, &.focus,
&:focus { &:focus {
color: #444444;
text-decoration: none; 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,
&:active { &:active {
background-image: none;
outline: 0; outline: 0;
text-decoration: none; background-image: none;
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); -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,
&[disabled] { &[disabled] {
cursor: default; cursor: not-allowed;
background-image: none; pointer-events: none;
@include opacity(65); opacity: 0.65;
@include box-shadow(none); filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
} }
&.btn-primary { &.btn-primary {
color: #FFF; color: #ffffff;
border-color: #189; background-color: #429bca;
text-shadow: 0 1px 1px #189; border-color: #358ebd;
@include linear-gradient(#4AC, #289);
&.hover, &.hover,
&:hover, &:hover,
&.disabled, &.disabled,
&[disabled] { &[disabled] {
color: #FFF; color: #ffffff;
background: #389; background-color: #3286b1;
border-color: #286e8e;
} }
} }
&.btn-success { &.btn-success {
color: #FFF; color: #ffffff;
border-color: #1A1; background-color: #5cb85c;
text-shadow: 0 1px 1px #FFF; border-color: #4cae4c;
text-shadow: 0 1px 1px #181;
@include linear-gradient(#62C452, #51a351);
&.hover, &.hover,
&:hover, &:hover,
&.disabled, &.disabled,
&[disabled] { &[disabled] {
color: #FFF; color: #ffffff;
background: #2A2; background-color: #47a447;
border-color: #398439;
} }
} }
&.btn-danger { &.btn-danger {
color: #FFF; color: #ffffff;
text-shadow: 0 1px 1px #811; background-color: #d9534f;
border-color: #BD362F; border-color: #d43f3a;
@include linear-gradient(#EE5F5B, #BD362F);
&.hover, &.hover,
&:hover, &:hover,
&.disabled, &.disabled,
&[disabled] { &[disabled] {
color: #FFF; color: #ffffff;
background: #A22; background-color: #d2322d;
border-color: #ac2925;
} }
} }
...@@ -138,4 +148,11 @@ ...@@ -138,4 +148,11 @@
margin-right: 7px; margin-right: 7px;
float: left; float: left;
} }
&.btn-block {
width: 100%;
margin: 0;
padding: 6px 0;
margin-bottom: 15px;
}
} }
/** COLORS **/ /** COLORS **/
.cgray { color: gray } .cgray { color: gray }
.clgray { color: #BBB }
.cred { color: #D12F19 } .cred { color: #D12F19 }
.cgreen { color: #4a2 } .cgreen { color: #4a2 }
.cblue { color: #29A } .cblue { color: #29A }
...@@ -88,6 +89,19 @@ pre.well-pre { ...@@ -88,6 +89,19 @@ pre.well-pre {
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); @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 **/ /** Big Labels **/
.state-label { .state-label {
font-size: 14px; font-size: 14px;
...@@ -109,3 +123,7 @@ pre.well-pre { ...@@ -109,3 +123,7 @@ pre.well-pre {
color: #FFF; color: #FFF;
} }
} }
.dropdown-menu > li > a {
text-shadow: none;
}
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
} }
.file-title { .file-title {
border-bottom: 1px solid #bbb; background: #DDD;
@include bg-dark-gray-gradient; border-bottom: 1px solid #CCC;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
margin: 0; margin: 0;
font-weight: normal; font-weight: normal;
......
...@@ -3,6 +3,16 @@ form { ...@@ -3,6 +3,16 @@ form {
label { label {
@extend .control-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 { ...@@ -49,3 +59,9 @@ fieldset legend {
font-size: 16px; font-size: 16px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.datetime-controls {
select {
width: 100px;
}
}
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to)); background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));
background-image: -webkit-linear-gradient($from, $to); background-image: -webkit-linear-gradient($from, $to);
background-image: -moz-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); background-image: -o-linear-gradient($from, $to);
} }
...@@ -45,6 +46,7 @@ ...@@ -45,6 +46,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1)); 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: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-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); background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
} }
...@@ -53,6 +55,7 @@ ...@@ -53,6 +55,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-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); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
} }
...@@ -60,6 +63,7 @@ ...@@ -60,6 +63,7 @@
background: #eee; background: #eee;
background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7); background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -moz-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); background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
} }
...@@ -85,10 +89,26 @@ ...@@ -85,10 +89,26 @@
} }
code { padding: 0 4px; } code { padding: 0 4px; }
h1 { margin-top: 30px;}
h2 { margin-top: 25px;} h1 {
h3 { margin-top: 20px;} margin-top: 45px;
h4 { margin-top: 15px;} 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 { blockquote p {
color: #888; color: #888;
...@@ -103,6 +123,16 @@ ...@@ -103,6 +123,16 @@
background: #EEE; background: #EEE;
} }
} }
code {
font-size: inherit;
font-weight: inherit;
color: #555;
}
li {
line-height: 1.5;
}
} }
@mixin page-title { @mixin page-title {
......
...@@ -21,3 +21,9 @@ ...@@ -21,3 +21,9 @@
.controls { margin-left: 130px; } .controls { margin-left: 130px; }
.form-actions { padding-left: 130px; background: #fff } .form-actions { padding-left: 130px; background: #fff }
} }
.broadcast-messages {
.message {
line-height: 2;
}
}
...@@ -16,36 +16,29 @@ ...@@ -16,36 +16,29 @@
.header { .header {
@extend .clearfix; @extend .clearfix;
background: #DDD;
border-bottom: 1px solid #CCC;
padding: 5px 5px 5px 10px; padding: 5px 5px 5px 10px;
color: #555; 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 { > span {
font-family: $monospace_font; font-family: $monospace_font;
font-size: 14px; font-size: 14px;
line-height: 30px; line-height: 2;
} }
a.view-file{ .view-file {
font-weight: bold; font-weight: bold;
float: right;
background-color: #EEE;
} }
.commit-short-id{ .commit-short-id {
font-family: $monospace_font; font-family: $monospace_font;
font-size: smaller; font-size: smaller;
} }
.file-mode{ .file-mode {
font-family: $monospace_font; font-family: $monospace_font;
} }
} }
...@@ -55,13 +48,13 @@ ...@@ -55,13 +48,13 @@
background: #FFF; background: #FFF;
color: #333; color: #333;
font-size: 12px; font-size: 12px;
.old{ .old {
span.idiff{ span.idiff {
background-color: #FAA; background-color: #FAA;
} }
} }
.new{ .new {
span.idiff{ span.idiff {
background-color: #AFA; background-color: #AFA;
} }
} }
...@@ -293,6 +286,7 @@ ...@@ -293,6 +286,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-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); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
ul, li{ ul, li{
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
li { li {
&.active { &.active {
a { a {
@include linear-gradient(#f5f5f5, #eee); background-color: #EEE;
border-bottom: 1px solid #EEE !important; border-bottom: 1px solid #EEE !important;
&:hover { &:hover {
background: #eee; background: #eee;
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
} }
.project-name, .group-name { .project-name, .group-name {
font-size: 16px; font-size: 15px;
} }
.arrow { .arrow {
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
.event-title { .event-title {
color: #333; color: #333;
font-weight: normal; font-weight: normal;
font-size: 15px; font-size: 14px;
.author_name { .author_name {
color: #333; color: #333;
} }
...@@ -60,14 +60,14 @@ ...@@ -60,14 +60,14 @@
color: #666; color: #666;
} }
.event-note { .event-note {
color: #555; color: #666;
margin-top: 5px; margin-top: 5px;
pre { pre {
border: none; border: none;
background: #f9f9f9; background: #f9f9f9;
border-radius: 0; border-radius: 0;
color: #555; color: #666;
margin: 0 20px; margin: 0 20px;
} }
...@@ -142,19 +142,19 @@ ...@@ -142,19 +142,19 @@
.filter_icon { .filter_icon {
a { a {
text-align:center; text-align:center;
border-left: 3px solid $primary_color; background: #EEE;
background: #f9f9f9;
margin-bottom: 10px; margin-bottom: 10px;
float: left; float: left;
padding: 9px 7px; padding: 9px 6px;
font-size: 18px; font-size: 18px;
width: 26px; width: 26px;
@include border-radius(3px);
} }
&.inactive { &.inactive {
a { a {
color: #DDD; color: #DDD;
border-left: 3px solid #EEE; background: #f9f9f9;
} }
} }
} }
......
...@@ -36,8 +36,8 @@ header { ...@@ -36,8 +36,8 @@ header {
float: left; float: left;
margin-right: 9px; margin-right: 9px;
position: relative; position: relative;
top: -5px; top: -3px;
padding-top: 5px; padding-top: 3px;
a { a {
float: left; float: left;
......
...@@ -110,9 +110,29 @@ ...@@ -110,9 +110,29 @@
.merge-request-angle { .merge-request-angle {
text-align: center; 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 { .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 @@ ...@@ -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 border-radius(4px);
@include bg-gray-gradient;
padding: 4px 7px;
border: 1px solid #CCC;
margin-bottom: 20px;
.btn { .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"] { input[type="text"] {
margin-left: 2px;
border: none;
border-radius: 0;
border-left: 1px solid #E1E1E1;
@extend .monospace; @extend .monospace;
border: 1px solid #BBB;
box-shadow: none; box-shadow: none;
margin-left: -1px; background: #FAFAFA;
background: #FFF; padding: 6px 10px;
} }
} }
...@@ -81,16 +132,16 @@ ul.nav.nav-projects-tabs { ...@@ -81,16 +132,16 @@ ul.nav.nav-projects-tabs {
.my-projects { .my-projects {
li { li {
.project-title {
font-size: 14px;
}
.project-info { .project-info {
margin-bottom: 10px; margin-bottom: 10px;
} }
.access-icon i { .access-icon {
color: #AAA; color: #AAA;
margin-left: 10px;
i {
color: #AAA;
}
} }
} }
} }
...@@ -115,3 +166,61 @@ ul.nav.nav-projects-tabs { ...@@ -115,3 +166,61 @@ ul.nav.nav-projects-tabs {
color: #777; 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.js selectbox style override **/
.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-container { .chosen-container {
min-width: 100px; min-width: 100px;
.chosen-search { .chosen-single {
input:focus { background: #EEE !important;
@include box-shadow(none); border: 1px solid #DDD !important;
} @include box-shadow(none !important);
@include border-radius(4px !important);
} }
.chosen-drop { .chosen-results li.highlighted {
margin: 7px 0; background: #29b;
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 { .chosen-drop {
@include bg-gray-gradient; margin-top: 10px;
input { border: 1px solid #DDD !important;
min-width: 165px; @include border-radius(4px !important);
border-color: #CCC;
}
}
}
}
.chosen-container .chosen-single,
.chosen-container.chosen-with-drop .chosen-single {
@include bg-light-gray-gradient;
div {
background: transparent;
border-left: none;
} }
span { .chosen-search input {
font-weight: normal; border: 1px solid #CCC !important;
@include box-shadow(none !important);
} }
} }
/** Select2 styling **/ /** Select2 styling **/
.select2-container .select2-choice { .select2-container .select2-choice {
background: #f1f1f1; @include bg-light-gray-gradient;
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);
} }
.select2-container .select2-choice div { .select2-container .select2-choice div {
......
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
background: #435; background: #435;
border-left: 1px solid #658; border-left: 1px solid #658;
} }
.nav > li > a {
color: #98B;
}
.search-input {
border-color: #98B;
}
} }
} }
} }
......
...@@ -23,12 +23,17 @@ ...@@ -23,12 +23,17 @@
background-color: #373D47; 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 @@ ...@@ -27,6 +27,12 @@
background: #234; background: #234;
border-left: 1px solid #456; border-left: 1px solid #456;
} }
.nav > li > a {
color: #89A;
}
.search-input {
border-color: #89A;
}
} }
} }
} }
......
...@@ -17,4 +17,3 @@ class BaseContext ...@@ -17,4 +17,3 @@ class BaseContext
abilities.allowed?(object, action, subject) abilities.allowed?(object, action, subject)
end end
end end
...@@ -18,6 +18,7 @@ class CommitLoadContext < BaseContext ...@@ -18,6 +18,7 @@ class CommitLoadContext < BaseContext
result[:note] = project.build_commit_note(commit) result[:note] = project.build_commit_note(commit)
result[:line_notes] = line_notes result[:line_notes] = line_notes
result[:notes_count] = project.notes.for_commit_id(commit.id).count result[:notes_count] = project.notes.for_commit_id(commit.id).count
result[:branches] = project.repository.branch_names_contains(commit.id)
begin begin
result[:suppress_diff] = true if commit.diff_suppress? && !params[:force_show_diff] 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 ...@@ -47,8 +47,6 @@ module Projects
@project.creator = current_user @project.creator = current_user
if @project.save if @project.save
@project.discover_default_branch
unless @project.group unless @project.group
@project.users_projects.create( @project.users_projects.create(
project_access: UsersProject::MASTER, project_access: UsersProject::MASTER,
......
...@@ -3,6 +3,16 @@ module Projects ...@@ -3,6 +3,16 @@ module Projects
def execute(role = :default) def execute(role = :default)
params[:project].delete(:namespace_id) params[:project].delete(:namespace_id)
params[:project].delete(:public) unless can?(current_user, :change_public_mode, project) 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) project.update_attributes(params[:project], as: role)
end end
end end
......
...@@ -10,11 +10,14 @@ class SearchContext ...@@ -10,11 +10,14 @@ class SearchContext
query = Shellwords.shellescape(query) if query.present? query = Shellwords.shellescape(query) if query.present?
return result unless query.present? return result unless query.present?
result[:projects] = Project.where("projects.id in (?) OR projects.public = true", project_ids).search(query).limit(20)
projects = Project.where(id: project_ids)
result[:projects] = projects.search(query).limit(20)
# Search inside single project # 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 project = projects.first if projects.length == 1
if params[:search_code].present? if params[:search_code].present?
...@@ -24,7 +27,6 @@ class SearchContext ...@@ -24,7 +27,6 @@ class SearchContext
result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20) result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20)
result[:wiki_pages] = [] result[:wiki_pages] = []
end end
result
end end
def result def result
......
class Admin::BackgroundJobsController < Admin::ApplicationController class Admin::BackgroundJobsController < Admin::ApplicationController
def show def show
@sidekiq_processes = `ps -U #{Settings.gitlab.user} -o euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep`
end end
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 ...@@ -2,5 +2,6 @@ class Admin::DashboardController < Admin::ApplicationController
def index def index
@projects = Project.order("created_at DESC").limit(10) @projects = Project.order("created_at DESC").limit(10)
@users = User.order("created_at DESC").limit(10) @users = User.order("created_at DESC").limit(10)
@groups = Group.order("created_at DESC").limit(10)
end end
end end
class Admin::ProjectsController < Admin::ApplicationController 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 def index
owner_id = params[:owner_id] owner_id = params[:owner_id]
...@@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController
end end
def show def show
@repository = @project.repository end
@group = @project.group
def transfer
result = ::Projects::TransferContext.new(@project, current_user, project: params).execute(:admin)
if result
redirect_to [:admin, @project]
else
render :show
end
end end
protected protected
...@@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController
@project = Project.find_with_namespace(id) @project = Project.find_with_namespace(id)
@project || render_404 @project || render_404
end end
def group
@group ||= project.group
end
def repository
@repository ||= project.repository
end
end end
...@@ -104,7 +104,7 @@ class GroupsController < ApplicationController ...@@ -104,7 +104,7 @@ class GroupsController < ApplicationController
# Dont allow unauthorized access to group # Dont allow unauthorized access to group
def authorize_read_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 return render_404
end end
end end
......
...@@ -23,4 +23,10 @@ class Projects::ApplicationController < ApplicationController ...@@ -23,4 +23,10 @@ class Projects::ApplicationController < ApplicationController
'public_projects' 'public_projects'
end end
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 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 ...@@ -7,9 +7,30 @@ class Projects::BlobController < Projects::ApplicationController
before_filter :authorize_code_access! before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :blob
def show 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
end end
...@@ -22,6 +22,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -22,6 +22,7 @@ class Projects::CommitController < Projects::ApplicationController
@note = result[:note] @note = result[:note]
@line_notes = result[:line_notes] @line_notes = result[:line_notes]
@branches = result[:branches]
@notes_count = result[:notes_count] @notes_count = result[:notes_count]
@target_type = :commit @target_type = :commit
@target_id = @commit.id @target_id = @commit.id
......
# Controller for edit a repository's file class Projects::EditTreeController < Projects::BaseTreeController
class Projects::EditTreeController < Projects::ApplicationController before_filter :require_branch_head
include ExtractsPath before_filter :blob
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :edit_requirements, only: [:show, :update]
def show def show
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end end
def update def update
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path) result = Files::UpdateContext.new(@project, current_user, params, @ref, @path).execute
updated_successfully = edit_file_action.commit!(
params[:content],
params[:commit_message],
params[:last_commit]
)
if updated_successfully if result[:status] == :success
redirect_to project_blob_path(@project, @id), notice: "Your changes have been successfully commited" flash[:notice] = "Your changes have been successfully commited"
redirect_to project_blob_path(@project, @id)
else else
flash[:notice] = "Your changes could not be commited, because the file has been changed" flash[:alert] = result[:error]
render :show render :show
end end
end end
private private
def edit_requirements def blob
@blob = @repository.blob_at(@commit.id, @path) @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
end end
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 class Projects::TeamMembersController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_admin_project! before_filter :authorize_admin_project!, except: :leave
layout "project_settings" layout "project_settings"
...@@ -45,6 +45,15 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -45,6 +45,15 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
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 def apply_import
giver = Project.find(params[:source_project_id]) giver = Project.find(params[:source_project_id])
status = @project.team.import(giver) status = @project.team.import(giver)
......
# Controller for viewing a repository's file structure # Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController class Projects::TreeController < Projects::BaseTreeController
include ExtractsPath
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
def show def show
return not_found! if tree.entries.empty? return not_found! if tree.entries.empty?
......
...@@ -62,10 +62,6 @@ class ProjectsController < ApplicationController ...@@ -62,10 +62,6 @@ class ProjectsController < ApplicationController
@events = event_filter.apply_filter(@events) @events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0) @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| respond_to do |format|
format.html do format.html do
if @project.empty_repo? if @project.empty_repo?
......
...@@ -84,8 +84,8 @@ module ApplicationHelper ...@@ -84,8 +84,8 @@ module ApplicationHelper
repository = @project.repository repository = @project.repository
options = [ options = [
["Branch", repository.branch_names ], ["Branches", repository.branch_names],
[ "Tag", repository.tag_names ] ["Tags", repository.tag_names]
] ]
# If reference is commit id - # If reference is commit id -
...@@ -126,6 +126,9 @@ module ApplicationHelper ...@@ -126,6 +126,9 @@ module ApplicationHelper
# Skip if user already created appropriate MR # Skip if user already created appropriate MR
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? 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 true
end end
...@@ -184,14 +187,6 @@ module ApplicationHelper ...@@ -184,14 +187,6 @@ module ApplicationHelper
Gitlab.config.extra Gitlab.config.extra
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
def search_placeholder def search_placeholder
if @project && @project.persisted? if @project && @project.persisted?
"Search in this project" "Search in this project"
...@@ -208,4 +203,8 @@ module ApplicationHelper ...@@ -208,4 +203,8 @@ module ApplicationHelper
line += "..." if lines.size > 1 line += "..." if lines.size > 1
line line
end end
def broadcast_message
BroadcastMessage.current
end
end end
...@@ -94,6 +94,17 @@ module CommitsHelper ...@@ -94,6 +94,17 @@ module CommitsHelper
crumbs.html_safe crumbs.html_safe
end 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 protected
# Private: Returns a link to a person. If the person has a matching user and # Private: Returns a link to a person. If the person has a matching user and
...@@ -114,7 +125,9 @@ module CommitsHelper ...@@ -114,7 +125,9 @@ module CommitsHelper
source_name source_name
end 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 = { options = {
class: "commit-#{options[:source]}-link has_tooltip", class: "commit-#{options[:source]}-link has_tooltip",
......
...@@ -127,4 +127,10 @@ module EventsHelper ...@@ -127,4 +127,10 @@ module EventsHelper
text = truncate(text, length: 150) text = truncate(text, length: 150)
sanitize(markdown(text), tags: %w(a img b pre p)) sanitize(markdown(text), tags: %w(a img b pre p))
end end
def event_commit_title(message)
escape_once(truncate(message.split("\n").first, length: 70))
rescue
"--broken encoding"
end
end end
...@@ -64,7 +64,9 @@ module GitlabMarkdownHelper ...@@ -64,7 +64,9 @@ module GitlabMarkdownHelper
# ref - name of the branch or reference, eg. stable # 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 # 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 # 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 = extract_paths(text)
paths.each do |file_path| paths.each do |file_path|
new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref) new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref)
...@@ -145,13 +147,18 @@ module GitlabMarkdownHelper ...@@ -145,13 +147,18 @@ module GitlabMarkdownHelper
def file_exists?(path) def file_exists?(path)
return false if path.nil? || path.empty? return false if path.nil? || path.empty?
File.exists?(Rails.root.join(path)) File.exists?(path_on_fs(path))
end end
# Check if the path is pointing to a directory(tree) or a file(blob) # 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 # eg. doc/api is directory and doc/README.md is file
def local_path(path) 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 end
# We will assume that if no ref exists we can point to master # We will assume that if no ref exists we can point to master
......
...@@ -2,4 +2,23 @@ module GroupsHelper ...@@ -2,4 +2,23 @@ module GroupsHelper
def remove_user_from_group_message(group, user) def remove_user_from_group_message(group, user)
"You are going to remove #{user.name} from #{group.name} Group. Are you sure?" "You are going to remove #{user.name} from #{group.name} Group. Are you sure?"
end 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 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 module NamespacesHelper
def namespaces_options(selected = :current_user, scope = :default) def namespaces_options(selected = :current_user, scope = :default)
groups = current_user.owned_groups.select {|n| n.type == 'Group'} groups = current_user.owned_groups
users = current_user.namespaces.reject {|n| n.type == 'Group'} users = [current_user.namespace]
group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] 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]} ] users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
...@@ -16,4 +16,13 @@ module NamespacesHelper ...@@ -16,4 +16,13 @@ module NamespacesHelper
grouped_options_for_select(options, selected) grouped_options_for_select(options, selected)
end 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 end
...@@ -45,7 +45,10 @@ module ProjectsHelper ...@@ -45,7 +45,10 @@ module ProjectsHelper
link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
end end
else 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
end end
...@@ -80,7 +83,7 @@ module ProjectsHelper ...@@ -80,7 +83,7 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC").all @project.milestones.active.order("due_date, title ASC").all
end end
def project_issues_trackers def project_issues_trackers(current_tracker = nil)
values = Project.issues_tracker.values.map do |tracker_key| values = Project.issues_tracker.values.map do |tracker_key|
if tracker_key.to_sym == :gitlab if tracker_key.to_sym == :gitlab
['GitLab', tracker_key] ['GitLab', tracker_key]
...@@ -89,7 +92,7 @@ module ProjectsHelper ...@@ -89,7 +92,7 @@ module ProjectsHelper
end end
end end
options_for_select(values) options_for_select(values, current_tracker)
end end
private private
...@@ -140,4 +143,38 @@ module ProjectsHelper ...@@ -140,4 +143,38 @@ module ProjectsHelper
# to calculate repo size - just show 'Unknown' # to calculate repo size - just show 'Unknown'
'unknown' 'unknown'
end 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 end
...@@ -5,7 +5,7 @@ module Emails ...@@ -5,7 +5,7 @@ module Emails
@group = @membership.group @group = @membership.group
mail(to: @membership.user.email, mail(to: @membership.user.email,
subject: subject("access to group was granted")) subject: subject("Access to group was granted"))
end end
end end
end end
...@@ -3,14 +3,14 @@ module Emails ...@@ -3,14 +3,14 @@ module Emails
def new_issue_email(recipient_id, issue_id) def new_issue_email(recipient_id, issue_id)
@issue = Issue.find(issue_id) @issue = Issue.find(issue_id)
@project = @issue.project @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 end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
@issue = Issue.find(issue_id) @issue = Issue.find(issue_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
@project = @issue.project @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 end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id) def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
...@@ -27,7 +27,7 @@ module Emails ...@@ -27,7 +27,7 @@ module Emails
@project = @issue.project @project = @issue.project
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_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 end
end end
...@@ -2,24 +2,24 @@ module Emails ...@@ -2,24 +2,24 @@ module Emails
module MergeRequests module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id) def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(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 end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_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 end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@updated_by = User.find updated_by_user_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 end
def merged_merge_request_email(recipient_id, merge_request_id) def merged_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(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
end end
......
...@@ -4,27 +4,27 @@ module Emails ...@@ -4,27 +4,27 @@ module Emails
@note = Note.find(note_id) @note = Note.find(note_id)
@commit = @note.noteable @commit = @note.noteable
@project = @note.project @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 end
def note_issue_email(recipient_id, note_id) def note_issue_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@issue = @note.noteable @issue = @note.noteable
@project = @note.project @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 end
def note_merge_request_email(recipient_id, note_id) def note_merge_request_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@merge_request = @note.noteable @merge_request = @note.noteable
@project = @note.project @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 end
def note_wall_email(recipient_id, note_id) def note_wall_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@project = @note.project @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 end
end end
...@@ -4,14 +4,14 @@ module Emails ...@@ -4,14 +4,14 @@ module Emails
@users_project = UsersProject.find user_project_id @users_project = UsersProject.find user_project_id
@project = @users_project.project @project = @users_project.project
mail(to: @users_project.user.email, mail(to: @users_project.user.email,
subject: subject("access to project was granted")) subject: subject("Access to project was granted"))
end end
def project_was_moved_email(project_id, user_id) def project_was_moved_email(project_id, user_id)
@user = User.find user_id @user = User.find user_id
@project = Project.find project_id @project = Project.find project_id
mail(to: @user.email, mail(to: @user.email,
subject: subject("project was moved")) subject: subject("Project was moved"))
end end
end 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 ...@@ -168,7 +168,7 @@ class Event < ActiveRecord::Base
end end
def valid_push? def valid_push?
data[:ref] data[:ref] && ref_name.present?
rescue => ex rescue => ex
false false
end end
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
# updated_at :datetime not null # updated_at :datetime not null
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # project_url :string(255)
# subdomain :string(255)
# room :string(255)
# #
require "flowdock-git-hook" require "flowdock-git-hook"
......
...@@ -25,7 +25,7 @@ class HipchatService < Service ...@@ -25,7 +25,7 @@ class HipchatService < Service
end end
def description def description
'Simple web-based real-time group chat' 'Private group chat and IM'
end end
def to_param def to_param
...@@ -71,5 +71,4 @@ class HipchatService < Service ...@@ -71,5 +71,4 @@ class HipchatService < Service
message message
end end
end end
...@@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base ...@@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base
include Issuable include Issuable
include InternalId include InternalId
ActsAsTaggableOn.strict_case_match = true
belongs_to :project belongs_to :project
validates :project, presence: true validates :project, presence: true
......
...@@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base ...@@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base
def send_update_instructions def send_update_instructions
projects.each(&:send_move_instructions) projects.each(&:send_move_instructions)
end end
def kind
type == 'Group' ? 'group' : 'user'
end
end end
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# creator_id :integer # creator_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null # issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null # wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null
...@@ -28,7 +27,9 @@ class Project < ActiveRecord::Base ...@@ -28,7 +27,9 @@ class Project < ActiveRecord::Base
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
extend Enumerize extend Enumerize
attr_accessible :name, :path, :description, :default_branch, :issues_tracker, :label_list, ActsAsTaggableOn.strict_case_match = true
attr_accessible :name, :path, :description, :issues_tracker, :label_list,
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
:wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin] :wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin]
...@@ -36,6 +37,8 @@ class Project < ActiveRecord::Base ...@@ -36,6 +37,8 @@ class Project < ActiveRecord::Base
acts_as_taggable_on :labels, :issues_default_labels acts_as_taggable_on :labels, :issues_default_labels
attr_accessor :new_default_branch
# Relations # Relations
belongs_to :creator, foreign_key: "creator_id", class_name: "User" belongs_to :creator, foreign_key: "creator_id", class_name: "User"
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
...@@ -125,7 +128,7 @@ class Project < ActiveRecord::Base ...@@ -125,7 +128,7 @@ class Project < ActiveRecord::Base
end end
def search query 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 end
def find_with_namespace(id) def find_with_namespace(id)
...@@ -146,7 +149,7 @@ class Project < ActiveRecord::Base ...@@ -146,7 +149,7 @@ class Project < ActiveRecord::Base
end end
def repository def repository
@repository ||= Repository.new(path_with_namespace, default_branch) @repository ||= Repository.new(path_with_namespace)
end end
def saved? def saved?
...@@ -303,14 +306,6 @@ class Project < ActiveRecord::Base ...@@ -303,14 +306,6 @@ class Project < ActiveRecord::Base
end end
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) def update_merge_requests(oldrev, newrev, ref, user)
return true unless ref =~ /heads/ return true unless ref =~ /heads/
branch_name = ref.gsub("refs/heads/", "") branch_name = ref.gsub("refs/heads/", "")
...@@ -450,4 +445,12 @@ class Project < ActiveRecord::Base ...@@ -450,4 +445,12 @@ class Project < ActiveRecord::Base
order('id DESC').limit(100). order('id DESC').limit(100).
update_all(updated_at: Time.now) update_all(updated_at: Time.now)
end 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 end
...@@ -3,7 +3,7 @@ class Repository ...@@ -3,7 +3,7 @@ class Repository
attr_accessor :raw_repository, :path_with_namespace 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 @path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
rescue Gitlab::Git::Repository::NoRepository rescue Gitlab::Git::Repository::NoRepository
...@@ -57,7 +57,7 @@ class Repository ...@@ -57,7 +57,7 @@ class Repository
def recent_branches(limit = 20) def recent_branches(limit = 20)
branches.sort do |a, b| 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[0..limit]
end end
......
...@@ -36,6 +36,11 @@ ...@@ -36,6 +36,11 @@
# notification_level :integer default(1), not null # notification_level :integer default(1), not null
# password_expires_at :datetime # password_expires_at :datetime
# created_by_id :integer # 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' require 'carrierwave/orm/activerecord'
...@@ -69,20 +74,20 @@ class User < ActiveRecord::Base ...@@ -69,20 +74,20 @@ class User < ActiveRecord::Base
# Namespace for personal projects # Namespace for personal projects
has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL' 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 # Profile
has_many :keys, dependent: :destroy has_many :keys, dependent: :destroy
# Groups # 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 :users_groups, dependent: :destroy
has_many :groups, through: :users_groups has_many :groups, through: :users_groups
has_many :owned_groups, through: :users_groups, source: :group, conditions: { users_groups: { group_access: UsersGroup::OWNER } }
# Projects # 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 :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
has_many :users_projects, dependent: :destroy has_many :users_projects, dependent: :destroy
has_many :issues, dependent: :destroy, foreign_key: :author_id has_many :issues, dependent: :destroy, foreign_key: :author_id
...@@ -93,11 +98,6 @@ class User < ActiveRecord::Base ...@@ -93,11 +98,6 @@ class User < ActiveRecord::Base
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue" 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 :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 # Validations
...@@ -247,7 +247,7 @@ class User < ActiveRecord::Base ...@@ -247,7 +247,7 @@ class User < ActiveRecord::Base
# Groups user has access to # Groups user has access to
def authorized_groups def authorized_groups
@authorized_groups ||= begin @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') Group.where(id: group_ids).order('namespaces.name ASC')
end end
end end
...@@ -256,7 +256,7 @@ class User < ActiveRecord::Base ...@@ -256,7 +256,7 @@ class User < ActiveRecord::Base
# Projects user has access to # Projects user has access to
def authorized_projects def authorized_projects
@authorized_projects ||= begin @authorized_projects ||= begin
project_ids = owned_projects.pluck(:id) project_ids = personal_projects.pluck(:id)
project_ids += groups_projects.pluck(:id) project_ids += groups_projects.pluck(:id)
project_ids += projects.pluck(:id) project_ids += projects.pluck(:id)
project_ids += groups.joins(:shared_projects).pluck(:project_id) project_ids += groups.joins(:shared_projects).pluck(:project_id)
...@@ -265,6 +265,12 @@ class User < ActiveRecord::Base ...@@ -265,6 +265,12 @@ class User < ActiveRecord::Base
end end
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 # Team membership in authorized projects
def tm_in_authorized_projects def tm_in_authorized_projects
UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id) UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
...@@ -337,7 +343,7 @@ class User < ActiveRecord::Base ...@@ -337,7 +343,7 @@ class User < ActiveRecord::Base
end end
def several_namespaces? def several_namespaces?
namespaces.many? || owned_groups.any? owned_groups.any?
end end
def namespace_id def namespace_id
...@@ -402,4 +408,9 @@ class User < ActiveRecord::Base ...@@ -402,4 +408,9 @@ class User < ActiveRecord::Base
self self
end end
def can_leave_project?(project)
project.namespace != namespace &&
project.project_member(self)
end
end end
...@@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver ...@@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver
def after_update(project) def after_update(project)
project.send_move_instructions if project.namespace_id_changed? project.send_move_instructions if project.namespace_id_changed?
project.rename_repo if project.path_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 end
def before_destroy(project) def before_destroy(project)
......
...@@ -24,7 +24,6 @@ class GitPushService ...@@ -24,7 +24,6 @@ class GitPushService
create_push_event create_push_event
project.ensure_satellite_exists project.ensure_satellite_exists
project.discover_default_branch
project.repository.expire_cache project.repository.expire_cache
if push_to_existing_branch?(ref, oldrev) if push_to_existing_branch?(ref, oldrev)
......
...@@ -18,6 +18,10 @@ class ProjectTransferService ...@@ -18,6 +18,10 @@ class ProjectTransferService
raise TransferError.new("Project with same path in target namespace already exists") raise TransferError.new("Project with same path in target namespace already exists")
end end
# Remove old satellite
project.satellite.destroy
# Apply new namespace id
project.namespace = new_namespace project.namespace = new_namespace
project.save! project.save!
...@@ -29,8 +33,8 @@ class ProjectTransferService ...@@ -29,8 +33,8 @@ class ProjectTransferService
# Move wiki repo also if present # Move wiki repo also if present
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
# create satellite repo # Create a new satellite (reload project from DB)
project.ensure_satellite_exists Project.find(project.id).ensure_satellite_exists
# clear project cached events # clear project cached events
project.reset_events_cache project.reset_events_cache
......
%h3.page-title Background Jobs %h3.page-title Background Jobs
%br %p.light GitLab use #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
%hr
.ui-box .ui-box
%iframe{src: sidekiq_path, width: '100%', height: 900, style: "border: none"} .title Sidekiq running processes
%h4 Sidekiq running processes .ui-box-body
- sidekiq_processes = `ps -eo euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep` - if @sidekiq_processes.empty?
- if sidekiq_processes.empty? %h4.cred
%b There are no running sidekiq processes %i.icon-warning-sign
%b Please restart GitLab There are no running sidekiq processes. Please restart GitLab
- else - else
.ui-box %table.table
%table.zebra-striped
%thead %thead
%th USER %th USER
%th %th
...@@ -25,7 +27,7 @@ ...@@ -25,7 +27,7 @@
%th %th
%th COMMAND %th COMMAND
%th %th
- sidekiq_processes.split("\n").each do |process| - @sidekiq_processes.split("\n").each do |process|
- next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/) - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
- data = process.gsub!(/\s+/m, '|').strip.split('|') - data = process.gsub!(/\s+/m, '|').strip.split('|')
%tr %tr
...@@ -33,6 +35,16 @@ ...@@ -33,6 +35,16 @@
%td= data.shift %td= data.shift
%td %td
%td= data.join(" ") %td= data.join(" ")
%b If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
%br .clearfix
%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. %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"}
%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 @@ ...@@ -51,6 +51,19 @@
= time_ago_in_words user.created_at = time_ago_in_words user.created_at
ago 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 .span4
%h4 Stats %h4 Stats
%hr %hr
...@@ -82,3 +95,34 @@ ...@@ -82,3 +95,34 @@
Milestones Milestones
%span.light.pull-right %span.light.pull-right
= Milestone.count = 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 @@ ...@@ -6,7 +6,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group.group_name_holder .control-group.group_name_holder
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Example Group", class: "input-xxlarge" = f.text_field :name, placeholder: "Example Group", class: "input-xxlarge"
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
.control-group.group_name_holder .control-group.group_name_holder
= f.label :path do = f.label :path do
%span.cred Group path is %span.cred Group path
.controls .controls
= f.text_field :path, placeholder: "example-group", class: "input-xxlarge danger" = f.text_field :path, placeholder: "example-group", class: "input-xxlarge danger"
%ul.cred %ul.cred
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group .control-group
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left" = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
.control-group.group-description-holder .control-group.group-description-holder
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
= @group.description = @group.description
%li %li
%span.light Created at: %span.light Created on:
%strong %strong
= @group.created_at.stamp("March 1, 1999") = @group.created_at.stamp("March 1, 1999")
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
= @project.creator.try(:name) || '(deleted)' = @project.creator.try(:name) || '(deleted)'
%li %li
%span.light Created at: %span.light Created on:
%strong %strong
= @project.created_at.stamp("March 1, 1999") = @project.created_at.stamp("March 1, 1999")
...@@ -74,6 +74,23 @@ ...@@ -74,6 +74,23 @@
%span.cgreen %span.cgreen
%i.icon-lock %i.icon-lock
Private 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 .span6
- if @group - if @group
.ui-box .ui-box
......
...@@ -54,15 +54,20 @@ ...@@ -54,15 +54,20 @@
.span9 .span9
%ul.bordered-list.my-projects.top-list %ul.bordered-list.my-projects.top-list
- @projects.each do |project| - @projects.each do |project|
%li %li.my-project-row
%h4.project-title %h4.project-title
%span.access-icon = link_to project_path(project), class: dom_class(project) do
= project.name_with_namespace
- if project.public - if project.public
%small.access-icon
= public_icon = public_icon
- else Public
= private_icon
= link_to project_path(project), class: dom_class(project) do - if current_user.can_leave_project?(project)
%strong= project.name_with_namespace .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 - if project.forked_from_project
%small.pull-right %small.pull-right
...@@ -81,6 +86,7 @@ ...@@ -81,6 +86,7 @@
%span.light Last activity: %span.light Last activity:
%span.date= project_last_activity(project) %span.date= project_last_activity(project)
- if @projects.blank? - if @projects.blank?
%li %li
%h3.nothing_here_message There are no projects here. %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 @@ ...@@ -7,5 +7,8 @@
%div %div
= f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password" = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password"
%div %div
.clearfix.append-bottom-10
= f.submit "Change my password", class: "btn btn-primary" = 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 @@ ...@@ -2,4 +2,4 @@
.commit-row-title .commit-row-title
= link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: '' = link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: ''
&nbsp; &nbsp;
= gfm escape_once(truncate(commit[:message], length: 70)) rescue "--broken encoding" = gfm event_commit_title(commit[:message])
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group .control-group
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left" = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
.title Remove group .title Remove group
.ui-box-body .ui-box-body
%p %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 %p
%strong Removed group can not be restored! %strong Removed group can not be restored!
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group .control-group
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left" = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
.control-group .control-group
.controls .controls
%ul %ul
%li Group is kind of directory for several projects %li A group is a collection of several projects
%li All created groups are private %li Groups are private by default
%li People within a group see only projects they have access to %li Members of a group may only view projects they have permission to access
%li All projects of group will be stored in a group directory %li Group project URLs are prefixed with the group namespace
%li You will be able to move existing projects into group %li Existing projects may be moved into a group
.form-actions .form-actions
= f.submit 'Create group', class: "btn btn-create" = f.submit 'Create group', class: "btn btn-create"
......
%h2.page-title .hero-unit
%h2
GitLab GitLab
%span.light Enterprise Edition %span.light Enterprise Edition
.pull-right
%span= Gitlab::VERSION %span= Gitlab::VERSION
%small= Gitlab::REVISION %small= Gitlab::REVISION
%p.slead %p.slead
Self Hosted Git Management GitLab is open source software to collaborate on code.
%br %br
Fast, secure and stable solution based on Ruby on Rails. 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 .row
.span4 .span4
......
- if broadcast_message.present?
.broadcast-message
%i.icon-bullhorn
= broadcast_message.message
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Dashboard" = render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application", :'data-page' => body_data_page } %body{class: "#{app_theme} application", :'data-page' => body_data_page }
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard" = render "layouts/head_panel", title: "Dashboard"
= render "layouts/flash" = render "layouts/flash"
%nav.main-nav %nav.main-nav
......
!!! 5 !!! 5
%html{ lang: "en"} %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} %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/head_panel", title: "group: #{@group.name}" = render "layouts/head_panel", title: "group: #{@group.name}"
= render "layouts/flash" = render "layouts/flash"
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
= link_to "Users", admin_users_path = link_to "Users", admin_users_path
= nav_link(controller: :logs) do = nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path = link_to "Logs", admin_logs_path
= nav_link(controller: :broadcast_messages) do
= link_to "Messages", admin_broadcast_messages_path
= nav_link(controller: :hooks) do = nav_link(controller: :hooks) do
= link_to "Hooks", admin_hooks_path = link_to "Hooks", admin_hooks_path
= nav_link(controller: :background_jobs) do = nav_link(controller: :background_jobs) do
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%i.icon-home %i.icon-home
- if project_nav_tab? :files - 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) = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
- if project_nav_tab? :commits - if project_nav_tab? :commits
......
!!! 5 !!! 5
%html{ lang: "en"} %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 } %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/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete" = render "layouts/init_auto_complete"
= render "layouts/flash" = render "layouts/flash"
......
%p %p
%strong #{@note.author_name} %strong #{@note.author_name}
left next message: wrote:
%cite{style: 'color: #666'} %cite{style: 'color: #666'}
= markdown(@note.note) = markdown(@note.note)
%p %p
= "New Merge Request !#{@merge_request.iid}" = "New Merge Request ##{@merge_request.iid}"
%p %p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p %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)) %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
......
%p %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) = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request)
%p %p
Assignee changed 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)) %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
Username Username
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
%p %p
Changing your username will change path to all personl projects! Changing your username will change path to all personal projects!
%div %div
= f.text_field :username, required: true, class: 'input-xlarge input-xpadding' = f.text_field :username, required: true, class: 'input-xlarge input-xpadding'
&nbsp; &nbsp;
...@@ -68,6 +68,6 @@ ...@@ -68,6 +68,6 @@
%li #{pluralize rp, 'personal project'} will be removed and cannot be restored %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
- if current_user.solo_owned_groups.present? - if current_user.solo_owned_groups.present?
%li %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(', ')} %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" = 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 @@ ...@@ -8,7 +8,7 @@
%span.light Title: %span.light Title:
%strong= @key.title %strong= @key.title
%li %li
%span.light Created at: %span.light Created on:
%strong= @key.created_at.stamp("Aug 21, 2011") %strong= @key.created_at.stamp("Aug 21, 2011")
.span8 .span8
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
&nbsp; &nbsp;
%span.file_name.js-avatar-filename File name... %span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-user-avatar-input hide" = 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 .form-actions
= f.submit 'Save changes', class: "btn btn-save" = 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 .btn-group.tree-btn-group
-# only show edit link for text files -# only show edit link for text files
- if @blob.text? - 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" = link_to "raw", project_raw_path(@project, @id), class: "btn btn-small", target: "_blank"
-# only show normal/blame view links for text files -# only show normal/blame view links for text files
- if @blob.text? - if @blob.text?
...@@ -10,3 +13,7 @@ ...@@ -10,3 +13,7 @@
- else - else
= link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty? = 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" = 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 @@ ...@@ -2,3 +2,6 @@
= render 'shared/ref_switcher', destination: 'blob', path: @path = render 'shared/ref_switcher', destination: 'blob', path: @path
%div#tree-holder.tree-holder %div#tree-holder.tree-holder
= render 'blob', blob: @blob = render 'blob', blob: @blob
- if allowed_tree_edit?
= render 'projects/blob/remove'
%ul.nav.nav-pills.nav-stacked %ul.nav.nav-pills.nav-stacked
= nav_link(path: 'branches#recent') do = 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 = nav_link(path: 'protected_branches#index') do
= link_to project_protected_branches_path(@project) do = link_to project_protected_branches_path(@project) do
Protected Protected
%i.icon-lock %i.icon-lock
.pull-right
= @project.protected_branches.count
= nav_link(path: 'branches#index') do = 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 %hr
......
...@@ -39,6 +39,19 @@ ...@@ -39,6 +39,19 @@
- @commit.parents.each do |parent| - @commit.parents.each do |parent|
= link_to parent.id[0...10], project_commit_path(@project, 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 .commit-box
%h3.commit-title %h3.commit-title
= gfm escape_once(@commit.title) = gfm escape_once(@commit.title)
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
%span= diff.old_path %span= diff.old_path
- if @commit.parent_ids.present? - 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 @ View file @
%span.commit-short-id= @commit.short_id(6) %span.commit-short-id= @commit.short_id(6)
- else - else
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-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 @ View file @
%span.commit-short-id= @commit.short_id(6) %span.commit-short-id= @commit.short_id(6)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
%fieldset %fieldset
.control-group.project_name_holder .control-group.project_name_holder
= f.label :name do = f.label :name do
Project name is Project name
.controls .controls
= f.text_field :name, placeholder: "Example Project", class: "span5" = f.text_field :name, placeholder: "Example Project", class: "span5"
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
Project description Project description
%span.light (optional) %span.light (optional)
.controls .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? - if @project.repository.exists? && @project.repository.branch_names.any?
.control-group .control-group
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
- if Project.issues_tracker.values.count > 1 - if Project.issues_tracker.values.count > 1
.control-group .control-group
= f.label :issues_tracker, "Issues tracker", class: 'control-label' = 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 .control-group
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label' = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
...@@ -124,7 +124,7 @@ ...@@ -124,7 +124,7 @@
%span Namespace %span Namespace
.controls .controls
.control-group .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 %ul
%li Be careful. Changing the project's namespace can have unintended side effects. %li Be careful. Changing the project's namespace can have unintended side effects.
%li You can only transfer the project to namespaces you manage. %li You can only transfer the project to namespaces you manage.
...@@ -144,7 +144,9 @@ ...@@ -144,7 +144,9 @@
%span Path %span Path
.controls .controls
.control-group .control-group
.input-append
= f.text_field :path = f.text_field :path
%span.add-on .git
%ul %ul
%li Be careful. Renaming a project's repository can have unintended side effects. %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. %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 - if @project.import? && !@project.imported
.save-project-loader .save-project-loader
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
%i.icon-code-fork %i.icon-code-fork
Fork Error! Fork Error!
%p %p
You are trying to fork You tried to fork
= link_to_project @project = 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? - if @forked_project && @forked_project.errors.any?
......
- frequency = @project.issues.tagged_with(label.name).count - frequency = @project.issues.tagged_with(label.name).count
%li %li
%strong
%span{class: "label #{label_css_class(label.name)}"} %span{class: "label #{label_css_class(label.name)}"}
%i.icon-tag %i.icon-tag
- if frequency.zero? - if frequency.zero?
...@@ -10,6 +9,5 @@ ...@@ -10,6 +9,5 @@
.pull-right .pull-right
- unless frequency.zero? - unless frequency.zero?
= link_to project_issues_path(label_name: label.name) do = link_to project_issues_path(label_name: label.name) do
%strong
= pluralize(frequency, 'issue') = pluralize(frequency, 'issue')
= "»" = "»"
...@@ -13,7 +13,6 @@ ...@@ -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'}) = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'})
.pull-left .pull-left
&nbsp; &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'}) = 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 .mr_source_commit.prepend-top-10
.span2 .span2
...@@ -26,7 +25,6 @@ ...@@ -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'}) = 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 .pull-left
&nbsp; &nbsp;
%i.icon-code-fork
= f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'}) = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'})
.mr_target_commit.prepend-top-10 .mr_target_commit.prepend-top-10
......
...@@ -13,13 +13,14 @@ ...@@ -13,13 +13,14 @@
.ui-box-body .ui-box-body
%div %div
%cite.cgray %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 - 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 - if @merge_request.milestone
- milestone = @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) %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
\.
- if @merge_request.description.present? - if @merge_request.description.present?
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
= form_for @project, remote: true do |f| = form_for @project, remote: true do |f|
.control-group.project-name-holder .control-group.project-name-holder
= f.label :name do = f.label :name do
%strong Project name is %strong Project name
.controls .controls
= f.text_field :name, placeholder: "Example Project", class: "input-xlarge", tabindex: 1, autofocus: true = f.text_field :name, placeholder: "Example Project", class: "input-xlarge", tabindex: 1, autofocus: true
%span.help-inline %span.help-inline
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
Description Description
%span.light (optional) %span.light (optional)
.controls .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 .control-group.project-public-holder
= f.label :public do = f.label :public do
%span Public project %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 %h3.page-title
- if @service.activated?
%span.cgreen
%i.icon-circle
- else
%span.cgray
%i.icon-circle-blank
= @service.title = @service.title
= boolean_to_icon @service.activated?
%p= @service.description %p= @service.description
......
...@@ -6,12 +6,8 @@ ...@@ -6,12 +6,8 @@
- @services.each do |service| - @services.each do |service|
%li %li
%h4 %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 = link_to edit_project_service_path(@project, service.to_param) do
= service.title = service.title
.pull-right
= boolean_to_icon service.activated?
%p= service.description %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 .row
.span9 .span9
...@@ -6,20 +34,34 @@ ...@@ -6,20 +34,34 @@
= render 'shared/event_filter' = render 'shared/event_filter'
.content_list .content_list
.loading.hide .loading.hide
.span3 .span3.project-side
.light-well .clearfix
%h3.page-title - if @project.forked_from_project
= @project.name .alert.alert-success
- if @project.description.present? %i.icon-code-fork.project-fork-icon
%p.light= @project.description 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
%p %span.light Created on
%span.light Repo size is
= repository_size
%p
%span.light Created at
#{@project.created_at.stamp('Aug 22, 2013')} #{@project.created_at.stamp('Aug 22, 2013')}
%p %p
%span.light Owned by %span.light Owned by
...@@ -27,19 +69,7 @@ ...@@ -27,19 +69,7 @@
#{link_to @project.group.name, @project.group} Group #{link_to @project.group.name, @project.group} Group
- else - else
#{link_to @project.owner_name, @project.owner} #{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? - if @project.gitlab_ci?
%hr %hr
......
...@@ -10,6 +10,12 @@ ...@@ -10,6 +10,12 @@
= link_to truncate(title, length: 40), project_tree_path(@project, path) = link_to truncate(title, length: 40), project_tree_path(@project, path)
- else - else
= link_to title, '#' = 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 %div#tree-content-holder.tree-content-holder
%table#tree-slider{class: "table_#{@hex_path} tree-table" } %table#tree-slider{class: "table_#{@hex_path} tree-table" }
......
...@@ -2,18 +2,14 @@ ...@@ -2,18 +2,14 @@
%h3.page-title %h3.page-title
Git access for Git access for
%strong= @gollum_wiki.path_with_namespace %strong= @gollum_wiki.path_with_namespace
= render 'main_links'
.content .form-horizontal.pull-right
.project_clone_panel .git-clone-holder
.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 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 %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 = text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
.git-empty
.git-empty
%fieldset %fieldset
%legend Install Gollum: %legend Install Gollum:
%pre.dark %pre.dark
......
.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-holder
.file-title .file-title
= link_to project_blob_path(@project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do = 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 @@ ...@@ -13,4 +13,4 @@
.results.prepend-top-10 .results.prepend-top-10
- if params[:search].present? - 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 active", :"data-clone" => @project.ssh_url_to_repo} SSH
%button{class: "btn", :"data-clone" => @project.http_url_to_repo}= gitlab_config.protocol.upcase %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 = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5", readonly: true
%span.add-on
- if @project.public
= public_icon
%span.cblue public
- else
= private_icon
%span.cgreen private
- if current_user.require_ssh_key? && alert.blank? && notice.blank? - if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key?
%p.error-message.centered .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 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 @@ ...@@ -8,9 +8,18 @@
= link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet' = 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 "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" = link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank"
.file-content.code
- unless @snippet.content.empty? - 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} %div{class: user_color_scheme_class}
= raw @snippet.colorize(formatter: :gitlab) = raw @snippet.colorize(formatter: :gitlab)
- else - else
.file-content.code
%p.nothing_here_message Empty file %p.nothing_here_message Empty file
...@@ -13,9 +13,20 @@ ...@@ -13,9 +13,20 @@
= f.label :title = f.label :title
.controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true .controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
.control-group .control-group
= f.label "Private?" = f.label "Access"
.controls .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 .control-group
.file-editor .file-editor
= f.label :file_name, "File" = f.label :file_name, "File"
...@@ -33,9 +44,10 @@ ...@@ -33,9 +44,10 @@
- else - else
= f.submit 'Save', class: "btn-save btn" = f.submit 'Save', class: "btn-save btn"
= link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
- unless @snippet.new_record? - 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 :javascript
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= link_to reliable_snippet_path(snippet) do = link_to reliable_snippet_path(snippet) do
= truncate(snippet.title, length: 60) = truncate(snippet.title, length: 60)
- if snippet.private? - if snippet.private?
%span.label.label-success %span.label.label-gray
%i.icon-lock %i.icon-lock
private private
%span.cgray.monospace.tiny.pull-right %span.cgray.monospace.tiny.pull-right
......
...@@ -14,7 +14,6 @@ class RepositoryImportWorker ...@@ -14,7 +14,6 @@ class RepositoryImportWorker
project.imported = true project.imported = true
project.save project.save
project.satellite.create unless project.satellite.exists? project.satellite.create unless project.satellite.exists?
project.discover_default_branch
else else
project.imported = false project.imported = false
end end
......
...@@ -78,7 +78,6 @@ module Gitlab ...@@ -78,7 +78,6 @@ module Gitlab
# #
# config.relative_url_root = "/gitlab" # config.relative_url_root = "/gitlab"
# Uncomment to enable rack attack middleware config.middleware.use Rack::Attack
# config.middleware.use Rack::Attack
end end
end end
...@@ -57,9 +57,9 @@ production: &base ...@@ -57,9 +57,9 @@ production: &base
## Automatic issue closing ## Automatic issue closing
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed. # 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. # 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 project features settings
default_projects_features: default_projects_features:
...@@ -155,7 +155,7 @@ production: &base ...@@ -155,7 +155,7 @@ production: &base
## Auth providers ## Auth providers
# Uncomment the following lines and fill in the data of the auth provider you want to use # 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: # 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 # 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. # arguments, followed by optional 'args' which can be either a hash or an array.
providers: providers:
......
# To enable rack-attack for your GitLab instance do the following: # 1. Rename this file to rack_attack.rb
# 1. In config/application.rb find and uncomment the following line: # 2. Review the paths_to_be_protected and add any other path you need protecting
# 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
# #
paths_to_be_protected = [ paths_to_be_protected = [
...@@ -11,6 +7,9 @@ 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/sign_in",
"#{Rails.application.config.relative_url_root}/users" "#{Rails.application.config.relative_url_root}/users"
] ]
Rack::Attack.throttle('protected paths', limit: 6, period: 60.seconds) do |req|
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? req.ip if paths_to_be_protected.include?(req.path) && req.post?
end
end end
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Gitlab::Application.config.session_store( Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks. :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', key: '_gitlab_session',
secure: Gitlab::Application.config.force_ssl, secure: Gitlab::Application.config.force_ssl,
httponly: true, httponly: true,
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# 2. Edit settings inside this file # 2. Edit settings inside this file
# 3. Restart GitLab instance # 3. Restart GitLab instance
# #
if Rails.env.production? do if Rails.env.production?
Gitlab::Application.config.action_mailer.delivery_method = :smtp Gitlab::Application.config.action_mailer.delivery_method = :smtp
ActionMailer::Base.smtp_settings = { ActionMailer::Base.smtp_settings = {
......
...@@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do ...@@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do
get :test get :test
end end
resources :broadcast_messages, only: [:index, :create, :destroy]
resource :logs, only: [:show] resource :logs, only: [:show]
resource :background_jobs, controller: 'background_jobs', 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" root to: "dashboard#index"
end end
...@@ -166,16 +173,18 @@ Gitlab::Application.routes.draw do ...@@ -166,16 +173,18 @@ Gitlab::Application.routes.draw do
end end
scope module: :projects do scope module: :projects do
resources :blob, only: [:show], constraints: {id: /.+/} resources :blob, only: [:show, :destroy], constraints: {id: /.+/}
resources :raw, only: [:show], constraints: {id: /.+/} resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' 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 :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
resources :compare, only: [:index, :create] resources :compare, only: [:index, :create]
resources :blame, only: [:show], constraints: {id: /.+/} resources :blame, only: [:show], constraints: {id: /.+/}
resources :network, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/} 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: /.+/} match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
resources :snippets, constraints: {id: /\d+/} do resources :snippets, constraints: {id: /\d+/} do
...@@ -222,14 +231,14 @@ Gitlab::Application.routes.draw do ...@@ -222,14 +231,14 @@ Gitlab::Application.routes.draw do
end end
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 collection do
get :recent get :recent, constraints: { id: Gitlab::Regex.git_reference_regex }
end end
end end
resources :tags, only: [:index, :new, :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: /[a-zA-Z.\/0-9_\-#%+]+/ } resources :protected_branches, only: [:index, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
resources :refs, only: [] do resources :refs, only: [] do
collection do collection do
...@@ -238,11 +247,11 @@ Gitlab::Application.routes.draw do ...@@ -238,11 +247,11 @@ Gitlab::Application.routes.draw do
member do member do
# tree viewer logs # 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", get "logs_tree/:path" => "refs#logs_tree",
as: :logs_file, as: :logs_file,
constraints: { constraints: {
id: /[a-zA-Z.0-9\/_\-#%+]+/, id: Gitlab::Regex.git_reference_regex,
path: /.*/ path: /.*/
} }
end end
...@@ -286,6 +295,7 @@ Gitlab::Application.routes.draw do ...@@ -286,6 +295,7 @@ Gitlab::Application.routes.draw do
resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do
collection do collection do
delete :leave
# Used for import team # Used for import team
# from another project # 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 @@ ...@@ -11,7 +11,16 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 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| create_table "deploy_keys_projects", :force => true do |t|
t.integer "deploy_key_id", :null => false t.integer "deploy_key_id", :null => false
...@@ -181,7 +190,6 @@ ActiveRecord::Schema.define(:version => 20131009115346) do ...@@ -181,7 +190,6 @@ ActiveRecord::Schema.define(:version => 20131009115346) do
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.integer "creator_id" t.integer "creator_id"
t.string "default_branch"
t.boolean "issues_enabled", :default => true, :null => false t.boolean "issues_enabled", :default => true, :null => false
t.boolean "wall_enabled", :default => true, :null => false t.boolean "wall_enabled", :default => true, :null => false
t.boolean "merge_requests_enabled", :default => true, :null => false t.boolean "merge_requests_enabled", :default => true, :null => false
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
### List projects ### List projects
Get a list of projects owned by the authenticated user. Get a list of projects accessible by the authenticated user.
``` ```
GET /projects GET /projects
...@@ -82,6 +82,22 @@ 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 single project
Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user. Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user.
...@@ -213,7 +229,6 @@ Parameters: ...@@ -213,7 +229,6 @@ Parameters:
+ `name` (required) - new project name + `name` (required) - new project name
+ `description` (optional) - short project description + `description` (optional) - short project description
+ `default_branch` (optional) - 'master' by default
+ `issues_enabled` (optional) + `issues_enabled` (optional)
+ `wall_enabled` (optional) + `wall_enabled` (optional)
+ `merge_requests_enabled` (optional) + `merge_requests_enabled` (optional)
......
...@@ -369,3 +369,42 @@ GET /projects/:id/repository/archive ...@@ -369,3 +369,42 @@ GET /projects/:id/repository/archive
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `sha` (optional) - The commit sha to download defaults to the tip of the default branch + `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 ...@@ -52,7 +52,7 @@ If you are not familiar with vim please skip this and keep using the default edi
Install the required packages: 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. Make sure you have the right version of Python installed.
...@@ -74,6 +74,33 @@ 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: # For reStructuredText markup language support install required package:
sudo apt-get install -y python-docutils 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 **Note:** In order to receive mail notifications, make sure to install a
mail server. By default, Debian is shipped with exim4 whereas Ubuntu 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: 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 ...@@ -91,8 +118,8 @@ Remove the old Ruby 1.8 if present
Download Ruby and compile it: Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby 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 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-p247 cd ruby-2.0.0-p353
./configure --disable-install-rdoc ./configure --disable-install-rdoc
make make
sudo make install sudo make install
...@@ -122,7 +149,7 @@ GitLab Shell is an ssh access and repository management software developed speci ...@@ -122,7 +149,7 @@ GitLab Shell is an ssh access and repository management software developed speci
cd gitlab-shell cd gitlab-shell
# switch to right version # 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 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 ...@@ -153,10 +180,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
cd /home/git/gitlab cd /home/git/gitlab
# Checkout to stable release # 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:** **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 ## Configure it
...@@ -167,6 +194,8 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version, ...@@ -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 # Make sure to change "localhost" to the fully-qualified domain name of your
# host serving GitLab where necessary # 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 sudo -u git -H editor config/gitlab.yml
# Make sure GitLab can write to the log/ and tmp/ directories # 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, ...@@ -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 # Copy the example Rack attack config
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb 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 # Configure Git global settings for git user, useful when editing via web
# Edit user.email according to what is set in gitlab.yml # Edit user.email according to what is set in gitlab.yml
sudo -u git -H git config --global user.name "GitLab" 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. ...@@ -236,8 +261,6 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
cd /home/git/gitlab cd /home/git/gitlab
sudo gem install charlock_holmes --version '0.6.9.4'
# For MySQL (note, the option says "without ... postgres") # For MySQL (note, the option says "without ... postgres")
sudo -u git -H bundle install --deployment --without development test postgres aws sudo -u git -H bundle install --deployment --without development test postgres aws
......
# Operating Systems # Operating Systems
## Linux
GitLab is developed for the Linux operating system. GitLab is developed for the Linux operating system.
GitLab officially supports (recent versions of) these Linux distributions: ## GitLab officially supports
- Ubuntu Linux - Ubuntu Linux
- Debian/GNU 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 - CentOS
- Oracle Linux
## Not officially supported
- Arch Linux
- Fedora - Fedora
- Gentoo - 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 There is nothing that prevents GitLab from running on other Unix operating systems.
systems. This means you may get it to work on systems running FreeBSD or OS X. This means you may get it to work on systems running FreeBSD or OS X.
**If you want to try, please proceed with caution!** 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 GitLab does **not** run on Windows and we have no plans of supporting it in the near future.
near future. Please consider using a virtual machine to run GitLab. 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. GitLab requires Ruby (MRI) 1.9.3 or 2.0+.
While it is generally possible to use other Rubies (like While it is generally possible to use other Rubies
[JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/)) it might require (like [JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/))
some work on your part. it might require some work since GitLab uses several Gems that have native extensions.
# Hardware requirements # 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). 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? ## Install guide up to date?
...@@ -58,13 +58,18 @@ Check if changed since last release (~22nd of last month depending on when last ...@@ -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. 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 * 5 days before release: feature freeze
- 3 days before release: UI freeze * 3 days before release: UI freeze
- 1 day before release: code 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 ## Last actions
1. Write a blog post (mention what GitLab is on the first line, select a MVP)
1. Update VERSION and CHANGELOG 1. Update VERSION and CHANGELOG
1. Create a git tag vX.X.X 1. Create a git tag vX.X.X
1. Publish the blog post 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 ...@@ -27,6 +27,11 @@ Feature: Admin active tab
Then the active main tab should be Logs Then the active main tab should be Logs
And no other main tabs should be active 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 Scenario: On Admin Hooks
Given I visit admin hooks page Given I visit admin hooks page
Then the active main tab should be Hooks 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 ...@@ -20,6 +20,10 @@ Feature: Project Browse files
And I click link "raw" And I click link "raw"
Then I should see raw file content 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 @javascript
Scenario: I can edit file Scenario: I can edit file
Given I click on "Gemfile.lock" file in repo Given I click on "Gemfile.lock" file in repo
......
...@@ -30,4 +30,8 @@ class AdminActiveTab < Spinach::FeatureSteps ...@@ -30,4 +30,8 @@ class AdminActiveTab < Spinach::FeatureSteps
Then 'the active main tab should be Resque' do Then 'the active main tab should be Resque' do
ensure_active_main_tab('Background Jobs') ensure_active_main_tab('Background Jobs')
end end
Then 'the active main tab should be Messages' do
ensure_active_main_tab('Messages')
end
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 ...@@ -3,42 +3,51 @@ class ProjectBrowseFiles < Spinach::FeatureSteps
include SharedProject include SharedProject
include SharedPaths 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 "app"
page.should have_content "history" page.should have_content "history"
page.should have_content "Gemfile" page.should have_content "Gemfile"
end 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") current_path.should == project_tree_path(@project, "8470d70")
page.should have_content "app" page.should have_content "app"
page.should have_content "history" page.should have_content "history"
page.should have_content "Gemfile" page.should have_content "Gemfile"
end end
Given 'I click on "Gemfile.lock" file in repo' do step 'I click on "Gemfile.lock" file in repo' do
click_link "Gemfile.lock" click_link "Gemfile.lock"
end end
Then 'I should see it content' do step 'I should see it content' do
page.should have_content "DEPENDENCIES" page.should have_content "DEPENDENCIES"
end end
And 'I click link "raw"' do step 'I click link "raw"' do
click_link "raw" click_link "raw"
end end
Then 'I should see raw file content' do step 'I should see raw file content' do
page.source.should == ValidCommit::BLOB_FILE page.source.should == ValidCommit::BLOB_FILE
end end
Given 'I click button "edit"' do step 'I click button "edit"' do
click_link 'edit' click_link 'edit'
end end
Then 'I can edit code' do step 'I can edit code' do
page.execute_script('editor.setValue("GitlabFileEditor")') page.execute_script('editor.setValue("GitlabFileEditor")')
page.evaluate_script('editor.getValue()').should == "GitlabFileEditor" page.evaluate_script('editor.getValue()').should == "GitlabFileEditor"
end 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 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 ...@@ -109,7 +109,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end end
And 'I click on the first commit in the merge request' do And 'I click on the first commit in the merge request' do
click_link merge_request.commits.first.short_id(8) click_link merge_request.commits.first.short_id(8)
end end
......
...@@ -23,7 +23,7 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -23,7 +23,7 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end end
step 'public project "Community"' do 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 end
step 'public empty project "Empty Public Project"' do step 'public empty project "Empty Public Project"' do
...@@ -49,7 +49,9 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -49,7 +49,9 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end end
step 'I should see project "Community" home page' do 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 end
private private
......
...@@ -105,6 +105,10 @@ module SharedPaths ...@@ -105,6 +105,10 @@ module SharedPaths
visit admin_logs_path visit admin_logs_path
end end
step 'I visit admin messages page' do
visit admin_broadcast_messages_path
end
step 'I visit admin hooks page' do step 'I visit admin hooks page' do
visit admin_hooks_path visit admin_hooks_path
end end
......
...@@ -47,7 +47,7 @@ module SharedProject ...@@ -47,7 +47,7 @@ module SharedProject
Then 'I should see project settings' do Then 'I should see project settings' do
current_path.should == edit_project_path(@project) 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:") page.should have_content("Features:")
end end
......
...@@ -19,7 +19,7 @@ class SnippetsFeature < Spinach::FeatureSteps ...@@ -19,7 +19,7 @@ class SnippetsFeature < Spinach::FeatureSteps
end end
And 'I click link "Destroy"' do And 'I click link "Destroy"' do
click_link "Destroy" click_link "Remove"
end end
And 'I submit new snippet "Personal snippet three"' do And 'I submit new snippet "Personal snippet three"' do
...@@ -46,7 +46,7 @@ class SnippetsFeature < Spinach::FeatureSteps ...@@ -46,7 +46,7 @@ class SnippetsFeature < Spinach::FeatureSteps
end end
And 'I uncheck "Private" checkbox' do And 'I uncheck "Private" checkbox' do
find(:xpath, "//input[@id='personal_snippet_private']").set true choose "Public"
click_button "Save" click_button "Save"
end end
......
...@@ -39,5 +39,8 @@ module API ...@@ -39,5 +39,8 @@ module API
mount DeployKeys mount DeployKeys
mount ProjectHooks mount ProjectHooks
mount Ldap mount Ldap
mount Services
mount Files
mount Namespaces
end end
end end
...@@ -5,16 +5,6 @@ module API ...@@ -5,16 +5,6 @@ module API
before { authorize_admin_project } before { authorize_admin_project }
resource :projects do 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 # Get a specific project's keys
# #
# Example Request: # Example Request:
......
...@@ -144,5 +144,9 @@ module API ...@@ -144,5 +144,9 @@ module API
class ProjectGroupLink < Grape::Entity class ProjectGroupLink < Grape::Entity
expose :id, :project_id, :group_id, :group_access expose :id, :project_id, :group_id, :group_access
end end
class Namespace < Grape::Entity
expose :id, :path, :kind
end
end 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 ...@@ -6,19 +6,23 @@ module API
SUDO_PARAM = :sudo SUDO_PARAM = :sudo
def current_user 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() identifier = sudo_identifier()
# If the sudo is the current user do nothing # If the sudo is the current user do nothing
if (identifier && !(@current_user.id == identifier || @current_user.username == identifier)) 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? 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) @current_user = User.by_username_or_id(identifier)
not_found!("No user id or username for: #{identifier}") if @current_user.nil? not_found!("No user id or username for: #{identifier}") if @current_user.nil?
end end
@current_user @current_user
end end
def sudo_identifier() def sudo_identifier()
identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER] identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
# Regex for integers # Regex for integers
if (!!(identifier =~ /^[0-9]+$/)) if (!!(identifier =~ /^[0-9]+$/))
identifier.to_i identifier.to_i
...@@ -29,6 +33,7 @@ module API ...@@ -29,6 +33,7 @@ module API
def set_current_user_for_thread def set_current_user_for_thread
Thread.current[:current_user] = current_user Thread.current[:current_user] = current_user
begin begin
yield yield
ensure 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 ...@@ -31,6 +31,16 @@ module API
present @projects, with: Entities::Project present @projects, with: Entities::Project
end 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 # Get a single project
# #
# Parameters: # Parameters:
...@@ -60,7 +70,6 @@ module API ...@@ -60,7 +70,6 @@ module API
# Parameters: # Parameters:
# name (required) - name for new project # name (required) - name for new project
# description (optional) - short project description # description (optional) - short project description
# default_branch (optional) - 'master' by default
# issues_enabled (optional) # issues_enabled (optional)
# wall_enabled (optional) # wall_enabled (optional)
# merge_requests_enabled (optional) # merge_requests_enabled (optional)
...@@ -75,7 +84,6 @@ module API ...@@ -75,7 +84,6 @@ module API
attrs = attributes_for_keys [:name, attrs = attributes_for_keys [:name,
:path, :path,
:description, :description,
:default_branch,
:issues_enabled, :issues_enabled,
:wall_enabled, :wall_enabled,
:merge_requests_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 ...@@ -13,20 +13,20 @@ module Backup
def dump def dump
case config["adapter"] case config["adapter"]
when /^mysql/ then 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 when "postgresql" then
pg_env pg_env
system("pg_dump #{config['database']} > #{db_file_name}") system('pg_dump', config['database'], out: db_file_name)
end end
end end
def restore def restore
case config["adapter"] case config["adapter"]
when /^mysql/ then 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 when "postgresql" then
pg_env pg_env
system("psql #{config['database']} -f #{db_file_name}") system('psql', config['database'], '-f', db_file_name)
end end
end end
...@@ -45,7 +45,7 @@ module Backup ...@@ -45,7 +45,7 @@ module Backup
'encoding' => '--default-character-set', 'encoding' => '--default-character-set',
'password' => '--password' '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 end
def pg_env def pg_env
......
module Backup module Backup
class Manager class Manager
BACKUP_CONTENTS = %w{repositories/ db/ uploads/ backup_information.yml}
def pack def pack
# saving additional informations # saving additional informations
s = {} s = {}
...@@ -16,7 +18,7 @@ module Backup ...@@ -16,7 +18,7 @@ module Backup
# create archive # create archive
print "Creating backup archive: #{s[:backup_created_at].to_i}_gitlab_backup.tar ... " 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 puts "done".green
else else
puts "failed".red puts "failed".red
...@@ -25,7 +27,7 @@ module Backup ...@@ -25,7 +27,7 @@ module Backup
def cleanup def cleanup
print "Deleting tmp directories ... " 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 puts "done".green
else else
puts "failed".red puts "failed".red
...@@ -44,7 +46,7 @@ module Backup ...@@ -44,7 +46,7 @@ module Backup
file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ } file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
file_list.sort.each do |timestamp| file_list.sort.each do |timestamp|
if Time.at(timestamp) < (Time.now - keep_time) 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 removed += 1
end end
end end
...@@ -75,7 +77,7 @@ module Backup ...@@ -75,7 +77,7 @@ module Backup
end end
print "Unpacking backup ... " print "Unpacking backup ... "
unless Kernel.system("tar -xf #{tar_file}") unless Kernel.system(*%W(tar -xf #{tar_file}))
puts "failed".red puts "failed".red
exit 1 exit 1
else else
......
...@@ -18,7 +18,7 @@ module Backup ...@@ -18,7 +18,7 @@ module Backup
# Create namespace dir if missing # Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace 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 puts "[DONE]".green
else else
puts "[FAILED]".red puts "[FAILED]".red
...@@ -30,7 +30,7 @@ module Backup ...@@ -30,7 +30,7 @@ module Backup
print " * #{wiki.path_with_namespace} ... " print " * #{wiki.path_with_namespace} ... "
if wiki.empty? if wiki.empty?
puts " [SKIPPED]".cyan 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 puts " [DONE]".green
else else
puts " [FAILED]".red puts " [FAILED]".red
...@@ -53,7 +53,7 @@ module Backup ...@@ -53,7 +53,7 @@ module Backup
project.namespace.ensure_dir_exist if project.namespace 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 puts "[DONE]".green
else else
puts "[FAILED]".red puts "[FAILED]".red
...@@ -63,7 +63,7 @@ module Backup ...@@ -63,7 +63,7 @@ module Backup
if File.exists?(path_to_bundle(wiki)) if File.exists?(path_to_bundle(wiki))
print " * #{wiki.path_with_namespace} ... " 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 puts " [DONE]".green
else else
puts " [FAILED]".red puts " [FAILED]".red
...@@ -73,7 +73,7 @@ module Backup ...@@ -73,7 +73,7 @@ module Backup
print 'Put GitLab hooks in repositories dirs'.yellow print 'Put GitLab hooks in repositories dirs'.yellow
gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") 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 puts " [DONE]".green
else else
puts " [FAILED]".red puts " [FAILED]".red
...@@ -103,5 +103,9 @@ module Backup ...@@ -103,5 +103,9 @@ module Backup
FileUtils.rm_rf(backup_repos_path) FileUtils.rm_rf(backup_repos_path)
FileUtils.mkdir_p(backup_repos_path) FileUtils.mkdir_p(backup_repos_path)
end end
def silent
{err: '/dev/null', out: '/dev/null'}
end
end end
end end
...@@ -38,6 +38,16 @@ module Grack ...@@ -38,6 +38,16 @@ module Grack
# Authentication with username and password # Authentication with username and password
login, password = @auth.credentials 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) @user = authenticate_user(login, password)
if @user if @user
...@@ -59,14 +69,7 @@ module Grack ...@@ -59,14 +69,7 @@ module Grack
end end
def authorized_git_request? def authorized_git_request?
# Git upload and receive authorize_request(service_name)
if @request.get?
authorize_request(@request.params['service'])
elsif @request.post?
authorize_request(File.basename(@request.path))
else
false
end
end end
def authenticate_user(login, password) def authenticate_user(login, password)
...@@ -79,27 +82,41 @@ module Grack ...@@ -79,27 +82,41 @@ module Grack
when 'git-upload-pack' when 'git-upload-pack'
project.public || can?(user, :download_code, project) project.public || can?(user, :download_code, project)
when'git-receive-pack' when'git-receive-pack'
refs.each do |ref|
action = if project.protected_branch?(ref) action = if project.protected_branch?(ref)
:push_code_to_protected_branches :push_code_to_protected_branches
else else
:push_code :push_code
end end
can?(user, action, project) return false unless can?(user, action, project)
end
true
else else
false false
end end
end end
def service_name
if @request.get?
@request.params['service']
elsif @request.post?
File.basename(@request.path)
else
nil
end
end
def project def project
@project ||= project_by_path(@request.path_info) @project ||= project_by_path(@request.path_info)
end end
def ref def refs
@ref ||= parse_ref @refs ||= parse_refs
end end
def parse_ref def parse_refs
input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
Zlib::GzipReader.new(@request.body).read Zlib::GzipReader.new(@request.body).read
else else
...@@ -108,7 +125,15 @@ module Grack ...@@ -108,7 +125,15 @@ module Grack
# Need to reset seek point # Need to reset seek point
@request.body.rewind @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 end
end end
...@@ -196,6 +196,15 @@ module Gitlab ...@@ -196,6 +196,15 @@ module Gitlab
Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git"
end 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 protected
def gitlab_shell_user_home def gitlab_shell_user_home
......
...@@ -18,10 +18,33 @@ module Gitlab ...@@ -18,10 +18,33 @@ module Gitlab
default_regex default_regex
end 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 protected
def default_regex 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 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 Gitlab
module Satellite module Satellite
# GitLab server-side file update and commit # GitLab server-side file update and commit
class EditFileAction < Action class EditFileAction < FileAction
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
# Updates the files content and creates a new commit for it # 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 the ref has been updated while editing the file
# Returns false if committing the change fails # 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 # Returns true otherwise
def commit!(content, commit_message, last_commit) def commit!(content, commit_message)
return false unless can_edit?(last_commit)
in_locked_and_timed_satellite do |repo| in_locked_and_timed_satellite do |repo|
prepare_satellite!(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}") repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
# update the file in the satellite's working dir # update the file in the satellite's working dir
file_path_in_satellite = File.join(repo.working_dir, file_path) 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) } File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
# commit the changes # commit the changes
...@@ -34,7 +33,7 @@ module Gitlab ...@@ -34,7 +33,7 @@ module Gitlab
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) 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 # will raise CommandFailed when push fails
repo.git.push({raise: true, timeout: true}, :origin, ref) repo.git.push({raise: true, timeout: true}, :origin, ref)
...@@ -45,13 +44,6 @@ module Gitlab ...@@ -45,13 +44,6 @@ module Gitlab
Gitlab::GitLogger.error(ex.message) Gitlab::GitLogger.error(ex.message)
false false
end 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 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 ...@@ -28,7 +28,7 @@ module Gitlab
in_locked_and_timed_satellite do |merge_repo| in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo) prepare_satellite!(merge_repo)
if merge_in_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 # will raise CommandFailed when push fails
merge_repo.git.push(default_options, :origin, merge_request.target_branch) merge_repo.git.push(default_options, :origin, merge_request.target_branch)
# remove source branch # remove source branch
......
...@@ -123,7 +123,7 @@ module Gitlab ...@@ -123,7 +123,7 @@ module Gitlab
remotes.each { |name| repo.git.remote(default_options,'rm', name)} remotes.each { |name| repo.git.remote(default_options,'rm', name)}
end end
# Updates the satellite from Gitolite # Updates the satellite from bare repo
# #
# Note: this will only update remote branches (i.e. origin/*) # Note: this will only update remote branches (i.e. origin/*)
def update_from_source! def update_from_source!
......
...@@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
def preprocess(full_document) def preprocess(full_document)
if @project 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 else
full_document full_document
end end
......
...@@ -20,8 +20,8 @@ RAILS_ENV="production" ...@@ -20,8 +20,8 @@ RAILS_ENV="production"
# Script variable names should be lower-case not to conflict with internal # Script variable names should be lower-case not to conflict with internal
# /bin/sh variables such as PATH, EDITOR or SHELL. # /bin/sh variables such as PATH, EDITOR or SHELL.
app_root="/home/git/gitlab"
app_user="git" app_user="git"
app_root="/home/$app_user/gitlab"
pid_path="$app_root/tmp/pids" pid_path="$app_root/tmp/pids"
socket_path="$app_root/tmp/sockets" socket_path="$app_root/tmp/sockets"
web_server_pid_path="$pid_path/unicorn.pid" web_server_pid_path="$pid_path/unicorn.pid"
...@@ -44,6 +44,7 @@ fi ...@@ -44,6 +44,7 @@ fi
### Init Script functions ### Init Script functions
## Gets the pids from the files
check_pids(){ check_pids(){
if ! mkdir -p "$pid_path"; then if ! mkdir -p "$pid_path"; then
echo "Could not create the path $pid_path needed to store the pids." echo "Could not create the path $pid_path needed to store the pids."
...@@ -62,12 +63,29 @@ check_pids(){ ...@@ -62,12 +63,29 @@ check_pids(){
fi 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. # 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. # Only after start() is run should the pids change. Sidekiq sets it's own pid.
check_pids 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_status(){
check_pids check_pids
# If the web server is running kill -0 $wpid returns true, or rather 0. # If the web server is running kill -0 $wpid returns true, or rather 0.
...@@ -84,9 +102,16 @@ check_status(){ ...@@ -84,9 +102,16 @@ check_status(){
else else
sidekiq_status="-1" sidekiq_status="-1"
fi 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_stale_pids(){
check_status check_status
# If there is a pid it is something else than 0, the service is running if # If there is a pid it is something else than 0, the service is running if
...@@ -94,7 +119,7 @@ check_stale_pids(){ ...@@ -94,7 +119,7 @@ check_stale_pids(){
if [ "$wpid" != "0" -a "$web_status" != "0" ]; then 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." 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 if ! rm "$web_server_pid_path"; then
echo "Unable to remove stale pid, exiting" echo "Unable to remove stale pid, exiting."
exit 1 exit 1
fi fi
fi fi
...@@ -107,7 +132,7 @@ check_stale_pids(){ ...@@ -107,7 +132,7 @@ check_stale_pids(){
fi 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(){ exit_if_not_running(){
check_stale_pids check_stale_pids
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
...@@ -116,86 +141,92 @@ exit_if_not_running(){ ...@@ -116,86 +141,92 @@ exit_if_not_running(){
fi fi
} }
# Starts Unicorn and Sidekiq. ## Starts Unicorn and Sidekiq if they're not running.
start() { start() {
check_stale_pids 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. # Then check if the service is running. If it is: don't start again.
if [ "$web_status" = "0" ]; then if [ "$web_status" = "0" ]; then
echo "The Unicorn web server already running with pid $wpid, not restarting." echo "The Unicorn web server already running with pid $wpid, not restarting."
else else
echo "Starting the GitLab Unicorn web server..."
# Remove old socket if it exists # Remove old socket if it exists
rm -f "$socket_path"/gitlab.socket 2>/dev/null rm -f "$socket_path"/gitlab.socket 2>/dev/null
# Start the webserver # Start the web server
RAILS_ENV=$RAILS_ENV script/web start RAILS_ENV=$RAILS_ENV script/web start &
fi fi
# If sidekiq is already running, don't start it again. # If sidekiq is already running, don't start it again.
if [ "$sidekiq_status" = "0" ]; then if [ "$sidekiq_status" = "0" ]; then
echo "The Sidekiq job dispatcher is already running with pid $spid, not restarting" echo "The Sidekiq job dispatcher is already running with pid $spid, not restarting"
else else
echo "Starting the GitLab Sidekiq event dispatcher..." RAILS_ENV=$RAILS_ENV script/background_jobs start &
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
fi fi
# Wait for the pids to be planted
wait_for_pids
# Finally check the status to tell wether or not GitLab is running # 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() { stop() {
exit_if_not_running 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 the Unicorn web server is running, tell it to stop;
if [ "$web_status" = "0" ]; then if [ "$web_status" = "0" ]; then
RAILS_ENV=$RAILS_ENV script/web stop 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."
fi fi
# And do the same thing for the Sidekiq. # And do the same thing for the Sidekiq.
if [ "$sidekiq_status" = "0" ]; then if [ "$sidekiq_status" = "0" ]; then
printf "Stopping Sidekiq job dispatcher."
RAILS_ENV=$RAILS_ENV script/background_jobs stop RAILS_ENV=$RAILS_ENV script/background_jobs stop
stopping=true
else
echo "The Sidekiq was not running, must have run out of breath."
fi fi
# If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script. # 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 sleep 1
check_status check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
printf "." printf "."
else if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
printf "\n" printf "\n"
break break
fi fi
done done
sleep 1 sleep 1
# Cleaning up unused pids # Cleaning up unused pids
rm "$web_server_pid_path" 2>/dev/null rm "$web_server_pid_path" 2>/dev/null
# rm "$sidekiq_pid_path" # Sidekiq seems to be cleaning up it's own pid. # 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 ## Prints the status of GitLab and it's components.
status() { print_status() {
check_status check_status
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
echo "GitLab is not running." echo "GitLab is not running."
return return
fi fi
if [ "$web_status" = "0" ]; then 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 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 fi
if [ "$sidekiq_status" = "0" ]; then if [ "$sidekiq_status" = "0" ]; then
echo "The GitLab Sidekiq job dispatcher with pid $spid is running." echo "The GitLab Sidekiq job dispatcher with pid $spid is running."
...@@ -207,6 +238,7 @@ status() { ...@@ -207,6 +238,7 @@ status() {
fi fi
} }
## Tells unicorn to reload it's config and Sidekiq to restart
reload(){ reload(){
exit_if_not_running exit_if_not_running
if [ "$wpid" = "0" ];then if [ "$wpid" = "0" ];then
...@@ -218,11 +250,12 @@ reload(){ ...@@ -218,11 +250,12 @@ reload(){
echo "Done." echo "Done."
echo "Restarting GitLab Sidekiq since it isn't capable of reloading its config..." echo "Restarting GitLab Sidekiq since it isn't capable of reloading its config..."
RAILS_ENV=$RAILS_ENV script/background_jobs restart RAILS_ENV=$RAILS_ENV script/background_jobs restart
# Waiting 2 seconds for sidekiq to write it.
sleep 2 wait_for_pids
status print_status
} }
## Restarts Sidekiq and Unicorn.
restart(){ restart(){
check_status check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
...@@ -232,7 +265,7 @@ restart(){ ...@@ -232,7 +265,7 @@ restart(){
} }
## Finally the input handling. ### Finally the input handling.
case "$1" in case "$1" in
start) start)
...@@ -248,7 +281,8 @@ case "$1" in ...@@ -248,7 +281,8 @@ case "$1" in
reload reload
;; ;;
status) status)
status print_status
exit $gitlab_status
;; ;;
*) *)
echo "Usage: service gitlab {start|stop|restart|reload|status}" echo "Usage: service gitlab {start|stop|restart|reload|status}"
......
...@@ -611,10 +611,7 @@ namespace :gitlab do ...@@ -611,10 +611,7 @@ namespace :gitlab do
end end
def gitlab_shell_version def gitlab_shell_version
gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION" Gitlab::Shell.new.version
if File.readable?(gitlab_shell_version_file)
File.read(gitlab_shell_version_file)
end
end end
def has_gitlab_shell3? def has_gitlab_shell3?
...@@ -648,7 +645,7 @@ namespace :gitlab do ...@@ -648,7 +645,7 @@ namespace :gitlab do
else else
puts "no".red puts "no".red
try_fixing_it( 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( for_more_information(
see_installation_guide_section("Install Init Script"), see_installation_guide_section("Install Init Script"),
...@@ -736,7 +733,7 @@ namespace :gitlab do ...@@ -736,7 +733,7 @@ namespace :gitlab do
end end
def check_gitlab_shell 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) current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_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 ...@@ -66,6 +66,7 @@ FactoryGirl.define do
after :create do |project| after :create do |project|
TestEnv.clear_repo_dir(project.namespace, project.path) TestEnv.clear_repo_dir(project.namespace, project.path)
TestEnv.reset_satellite_dir
TestEnv.create_repo(project.namespace, project.path) TestEnv.create_repo(project.namespace, project.path)
end 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
#
# 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' 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 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 end
...@@ -7,5 +7,17 @@ describe ProjectsHelper do ...@@ -7,5 +7,17 @@ describe ProjectsHelper do
"<option value=\"redmine\">Redmine</option>\n" \ "<option value=\"redmine\">Redmine</option>\n" \
"<option value=\"gitlab\">GitLab</option>" "<option value=\"gitlab\">GitLab</option>"
end 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
end end
...@@ -110,7 +110,7 @@ describe Notify do ...@@ -110,7 +110,7 @@ describe Notify do
it_behaves_like 'an assignee email' it_behaves_like 'an assignee email'
it 'has the correct subject' do 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 end
it 'contains a link to the new issue' do it 'contains a link to the new issue' do
...@@ -126,7 +126,7 @@ describe Notify do ...@@ -126,7 +126,7 @@ describe Notify do
it_behaves_like 'a multiple recipients email' it_behaves_like 'a multiple recipients email'
it 'has the correct subject' do 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 end
it 'contains the name of the previous assignee' do it 'contains the name of the previous assignee' do
...@@ -148,7 +148,7 @@ describe Notify do ...@@ -148,7 +148,7 @@ describe Notify do
subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) }
it 'has the correct subject' do 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 end
it 'contains the new status' do it 'contains the new status' do
...@@ -175,7 +175,7 @@ describe Notify do ...@@ -175,7 +175,7 @@ describe Notify do
it_behaves_like 'an assignee email' it_behaves_like 'an assignee email'
it 'has the correct subject' do 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 end
it 'contains a link to the new merge request' do it 'contains a link to the new merge request' do
...@@ -199,7 +199,7 @@ describe Notify do ...@@ -199,7 +199,7 @@ describe Notify do
it_behaves_like 'a multiple recipients email' it_behaves_like 'a multiple recipients email'
it 'has the correct subject' do 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 end
it 'contains the name of the previous assignee' do it 'contains the name of the previous assignee' do
...@@ -224,7 +224,7 @@ describe Notify do ...@@ -224,7 +224,7 @@ describe Notify do
subject { Notify.project_was_moved_email(project.id, user.id) } subject { Notify.project_was_moved_email(project.id, user.id) }
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /project was moved/ should have_subject /Project was moved/
end end
it 'contains name of project' do it 'contains name of project' do
...@@ -244,7 +244,7 @@ describe Notify do ...@@ -244,7 +244,7 @@ describe Notify do
user: user) } user: user) }
subject { Notify.project_access_granted_email(users_project.id) } subject { Notify.project_access_granted_email(users_project.id) }
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /access to project was granted/ should have_subject /Access to project was granted/
end end
it 'contains name of project' do it 'contains name of project' do
should have_body_text /#{project.name}/ should have_body_text /#{project.name}/
...@@ -302,7 +302,7 @@ describe Notify do ...@@ -302,7 +302,7 @@ describe Notify do
it_behaves_like 'a note email' it_behaves_like 'a note email'
it 'has the correct subject' do 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 end
it 'contains a link to the commit' do it 'contains a link to the commit' do
...@@ -320,7 +320,7 @@ describe Notify do ...@@ -320,7 +320,7 @@ describe Notify do
it_behaves_like 'a note email' it_behaves_like 'a note email'
it 'has the correct subject' do 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 end
it 'contains a link to the merge request note' do it 'contains a link to the merge request note' do
...@@ -338,7 +338,7 @@ describe Notify do ...@@ -338,7 +338,7 @@ describe Notify do
it_behaves_like 'a note email' it_behaves_like 'a note email'
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /note for issue ##{issue.iid}/ should have_subject /Note for issue ##{issue.iid}/
end end
it 'contains a link to the issue note' do it 'contains a link to the issue note' do
...@@ -356,7 +356,7 @@ describe Notify do ...@@ -356,7 +356,7 @@ describe Notify do
subject { Notify.group_access_granted_email(membership.id) } subject { Notify.group_access_granted_email(membership.id) }
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /access to group was granted/ should have_subject /Access to group was granted/
end end
it 'contains name of project' do it 'contains name of project' do
...@@ -367,4 +367,28 @@ describe Notify do ...@@ -367,4 +367,28 @@ describe Notify do
should have_body_text /#{membership.human_access}/ should have_body_text /#{membership.human_access}/
end end
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 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 @@ ...@@ -11,6 +11,8 @@
# updated_at :datetime not null # updated_at :datetime not null
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # project_url :string(255)
# subdomain :string(255)
# room :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -116,13 +116,13 @@ describe MergeRequest do ...@@ -116,13 +116,13 @@ describe MergeRequest do
end end
it 'accesses the set of issues that will be closed on acceptance' do 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) subject.closes_issues.should == [issue0, issue1].sort_by(&:id)
end end
it 'only lists issues as to be closed if it targets the default branch' do 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.target_branch = 'something-else'
subject.closes_issues.should be_empty subject.closes_issues.should be_empty
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# creator_id :integer # creator_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null # issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null # wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null
......
...@@ -36,6 +36,11 @@ ...@@ -36,6 +36,11 @@
# notification_level :integer default(1), not null # notification_level :integer default(1), not null
# password_expires_at :datetime # password_expires_at :datetime
# created_by_id :integer # 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' require 'spec_helper'
...@@ -135,7 +140,6 @@ describe User do ...@@ -135,7 +140,6 @@ describe User do
end end
it { @user.several_namespaces?.should be_true } it { @user.several_namespaces?.should be_true }
it { @user.namespaces.should include(@user.namespace) }
it { @user.authorized_groups.should == [@group] } it { @user.authorized_groups.should == [@group] }
it { @user.owned_groups.should == [@group] } it { @user.owned_groups.should == [@group] }
end end
...@@ -162,7 +166,6 @@ describe User do ...@@ -162,7 +166,6 @@ describe User do
end end
it { @user.several_namespaces?.should be_false } it { @user.several_namespaces?.should be_false }
it { @user.namespaces.should == [@user.namespace] }
end end
describe 'blocking user' do 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 ...@@ -36,6 +36,32 @@ describe API::API do
end end
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 describe "POST /projects" do
context "maximum number of projects reached" do context "maximum number of projects reached" do
before do before do
...@@ -91,7 +117,6 @@ describe API::API do ...@@ -91,7 +117,6 @@ describe API::API do
it "should assign attributes to project" do it "should assign attributes to project" do
project = attributes_for(:project, { project = attributes_for(:project, {
description: Faker::Lorem.sentence, description: Faker::Lorem.sentence,
default_branch: 'stable',
issues_enabled: false, issues_enabled: false,
wall_enabled: false, wall_enabled: false,
merge_requests_enabled: false, merge_requests_enabled: false,
...@@ -110,16 +135,13 @@ describe API::API do ...@@ -110,16 +135,13 @@ describe API::API do
project = attributes_for(:project, { public: true }) project = attributes_for(:project, { public: true })
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_true json_response['public'].should be_true
end end
it "should set a project as private" do it "should set a project as private" do
project = attributes_for(:project, { public: false }) project = attributes_for(:project, { public: false })
post api("/projects", user), project post api("/projects", user), project
json_response['public'].should be_false json_response['public'].should be_false
end end
end end
describe "POST /projects/user/:id" do describe "POST /projects/user/:id" do
...@@ -146,7 +168,6 @@ describe API::API do ...@@ -146,7 +168,6 @@ describe API::API do
it "should assign attributes to project" do it "should assign attributes to project" do
project = attributes_for(:project, { project = attributes_for(:project, {
description: Faker::Lorem.sentence, description: Faker::Lorem.sentence,
default_branch: 'stable',
issues_enabled: false, issues_enabled: false,
wall_enabled: false, wall_enabled: false,
merge_requests_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 ...@@ -138,12 +138,24 @@ end
describe Projects::BranchesController, "routing" do describe Projects::BranchesController, "routing" do
it "to #branches" do it "to #branches" do
get("/gitlab/gitlabhq/branches").should route_to('projects/branches#index', project_id: 'gitlab/gitlabhq') 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
end end
describe Projects::TagsController, "routing" do describe Projects::TagsController, "routing" do
it "to #tags" do it "to #tags" do
get("/gitlab/gitlabhq/tags").should route_to('projects/tags#index', project_id: 'gitlab/gitlabhq') 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
end end
...@@ -183,9 +195,11 @@ describe Projects::RefsController, "routing" do ...@@ -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/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%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%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/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%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%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') 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
end end
......
...@@ -45,6 +45,7 @@ module TestEnv ...@@ -45,6 +45,7 @@ module TestEnv
def disable_mailer def disable_mailer
NotificationService.any_instance.stub(mailer: double.as_null_object) NotificationService.any_instance.stub(mailer: double.as_null_object)
end end
def enable_mailer def enable_mailer
NotificationService.any_instance.unstub(:mailer) NotificationService.any_instance.unstub(:mailer)
end end
...@@ -68,7 +69,8 @@ module TestEnv ...@@ -68,7 +69,8 @@ module TestEnv
remove_repository: true, remove_repository: true,
update_repository_head: true, update_repository_head: true,
add_key: true, add_key: true,
remove_key: true remove_key: true,
version: '6.3.0'
) )
Gitlab::Satellite::Satellite.any_instance.stub( Gitlab::Satellite::Satellite.any_instance.stub(
...@@ -96,6 +98,15 @@ module TestEnv ...@@ -96,6 +98,15 @@ module TestEnv
FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git") FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git")
end 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 # Create a repo and it's satellite
def create_repo(namespace, name) def create_repo(namespace, name)
setup_stubs 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