Commit bf0de1a5 authored by Jan-Willem van der Meer's avatar Jan-Willem van der Meer

Merge remote-tracking branch 'origin/master' into feature-oauth-refactoring

parents b18d1c27 78ec7d9c
...@@ -5,6 +5,8 @@ targets: ...@@ -5,6 +5,8 @@ targets:
debian-7: &wheezy debian-7: &wheezy
build_dependencies: build_dependencies:
- libicu-dev - libicu-dev
- cmake
- pkg-config
dependencies: dependencies:
- libicu48 - libicu48
- libpcre3 - libpcre3
...@@ -13,6 +15,8 @@ targets: ...@@ -13,6 +15,8 @@ targets:
ubuntu-14.04: ubuntu-14.04:
build_dependencies: build_dependencies:
- libicu-dev - libicu-dev
- cmake
- pkg-config
dependencies: dependencies:
- libicu52 - libicu52
- libpcre3 - libpcre3
...@@ -20,6 +24,8 @@ targets: ...@@ -20,6 +24,8 @@ targets:
centos-6: centos-6:
build_dependencies: build_dependencies:
- libicu-devel - libicu-devel
- cmake
- pkgconfig
dependencies: dependencies:
- libicu - libicu
- pcre - pcre
......
language: ruby
cache:
directories:
- vendor/bundle
env:
global:
- TRAVIS=true
matrix:
- TASK=spinach_project DB=mysql
- TASK=spinach_other DB=mysql
- TASK=spec:api DB=mysql
- TASK=spec:feature DB=mysql
- TASK=spec:other DB=mysql
- TASK=jasmine:ci DB=mysql
- TASK=spinach_project DB=postgresql
- TASK=spinach_other DB=postgresql
- TASK=spec:api DB=postgresql
- TASK=spec:feature DB=postgresql
- TASK=spec:other DB=postgresql
- TASK=jasmine:ci DB=postgresql
before_install:
- sudo apt-get install libicu-dev -y
install:
- "travis_retry bundle config build.nokogiri --use-system-libraries"
- "travis_retry bundle install --deployment --without production --retry 5"
branches:
only:
- 'master'
rvm:
- 2.0.0
services:
- redis-server
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
- "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
script: "bundle exec rake $TASK --trace"
notifications:
email: false
git:
depth: 10
...@@ -10,9 +10,26 @@ v 7.3.0 ...@@ -10,9 +10,26 @@ v 7.3.0
- Support Unix domain sockets for Redis - Support Unix domain sockets for Redis
- Store session Redis keys in 'session:gitlab:' namespace - Store session Redis keys in 'session:gitlab:' namespace
- Deprecate LDAP account takeover based on partial LDAP email / GitLab username match - Deprecate LDAP account takeover based on partial LDAP email / GitLab username match
- Use /bin/sh instead of Bash in bin/web, bin/background_jobs (Pavel Novitskiy)
- Keyboard shortcuts for productivity (Robert Schilling) - Keyboard shortcuts for productivity (Robert Schilling)
- API: filter issues by state (Julien Bianchi) - API: filter issues by state (Julien Bianchi)
- API: filter issues by labels (Julien Bianchi)
- Add system hook for ssh key changes - Add system hook for ssh key changes
- Add blob permalink link (Ciro Santilli)
- Create annotated tags through UI and API (Sean Edge)
- Snippets search (Charles Bushong)
- Comment new push to existing MR
- Add 'ci' to the blacklist of forbidden names
- Improve text filtering on issues page
- Comment & Close button
- Process git push --all much faster
- Don't allow edit of system notes
- Project wiki search (Ralf Seidler)
- Enabled Shibboleth authentication support (Matus Banas)
v 7.2.1
- Delete orphaned labels during label migration (James Brooks)
- Security: prevent XSS with stricter MIME types for raw repo files
v 7.2.0 v 7.2.0
- Explore page - Explore page
......
...@@ -27,6 +27,7 @@ gem 'omniauth', "~> 1.1.3" ...@@ -27,6 +27,7 @@ gem 'omniauth', "~> 1.1.3"
gem 'omniauth-google-oauth2' gem 'omniauth-google-oauth2'
gem 'omniauth-twitter' gem 'omniauth-twitter'
gem 'omniauth-github' gem 'omniauth-github'
gem 'omniauth-shibboleth'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
...@@ -36,7 +37,7 @@ gem "gitlab_git", '~> 6.0' ...@@ -36,7 +37,7 @@ gem "gitlab_git", '~> 6.0'
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
# LDAP Auth # LDAP Auth
gem 'gitlab_omniauth-ldap', '1.0.4', require: "omniauth-ldap" gem 'gitlab_omniauth-ldap', '1.1.0', require: "omniauth-ldap"
# Git Wiki # Git Wiki
gem 'gollum-lib', '~> 3.0.0' gem 'gollum-lib', '~> 3.0.0'
......
...@@ -168,7 +168,7 @@ GEM ...@@ -168,7 +168,7 @@ GEM
multi_json multi_json
gitlab-grack (2.0.0.pre) gitlab-grack (2.0.0.pre)
rack (~> 1.5.1) rack (~> 1.5.1)
gitlab-grit (2.6.10) gitlab-grit (2.6.11)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
...@@ -186,8 +186,8 @@ GEM ...@@ -186,8 +186,8 @@ GEM
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
rugged (~> 0.21.0) rugged (~> 0.21.0)
gitlab_meta (7.0) gitlab_meta (7.0)
gitlab_omniauth-ldap (1.0.4) gitlab_omniauth-ldap (1.1.0)
net-ldap (~> 0.3.1) net-ldap (~> 0.7.0)
omniauth (~> 1.0) omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1) pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1) rubyntlm (~> 0.1.1)
...@@ -292,7 +292,7 @@ GEM ...@@ -292,7 +292,7 @@ GEM
multi_xml (0.5.5) multi_xml (0.5.5)
multipart-post (1.2.0) multipart-post (1.2.0)
mysql2 (0.3.16) mysql2 (0.3.16)
net-ldap (0.3.1) net-ldap (0.7.0)
net-scp (1.1.2) net-scp (1.1.2)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (2.8.0) net-ssh (2.8.0)
...@@ -321,6 +321,8 @@ GEM ...@@ -321,6 +321,8 @@ GEM
omniauth-oauth2 (1.1.1) omniauth-oauth2 (1.1.1)
oauth2 (~> 0.8.0) oauth2 (~> 0.8.0)
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-shibboleth (1.1.1)
omniauth (>= 1.0.0)
omniauth-twitter (1.0.1) omniauth-twitter (1.0.1)
multi_json (~> 1.3) multi_json (~> 1.3)
omniauth-oauth (~> 1.0) omniauth-oauth (~> 1.0)
...@@ -616,7 +618,7 @@ DEPENDENCIES ...@@ -616,7 +618,7 @@ DEPENDENCIES
gitlab_emoji (~> 0.0.1.1) gitlab_emoji (~> 0.0.1.1)
gitlab_git (~> 6.0) gitlab_git (~> 6.0)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.0.4) gitlab_omniauth-ldap (= 1.1.0)
gollum-lib (~> 3.0.0) gollum-lib (~> 3.0.0)
gon (~> 5.0.0) gon (~> 5.0.0)
grape (~> 0.6.1) grape (~> 0.6.1)
...@@ -644,6 +646,7 @@ DEPENDENCIES ...@@ -644,6 +646,7 @@ DEPENDENCIES
omniauth-github omniauth-github
omniauth-google-oauth2 omniauth-google-oauth2
omniauth-twitter omniauth-twitter
omniauth-shibboleth
org-ruby org-ruby
pg pg
poltergeist (~> 1.5.1) poltergeist (~> 1.5.1)
......
...@@ -87,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea ...@@ -87,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea
### Issue fixed in newer version ### Issue fixed in newer version
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Improperly formatted merge request ### Improperly formatted merge request
......
# GitLab # ![logo](https://about.gitlab.com/images/gitlab_logo.png) GitLab
## Open source software to collaborate on code ## Open source software to collaborate on code
![logo](https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/gitlab_logo.png) ![Animated screenshots](https://about.gitlab.com/images/animated/compiled.gif)
![animated-screenshots](https://gist.github.com/fnkr/2f9badd56bfe0ed04ee7/raw/4f48806fbae97f556c2f78d8c2d299c04500cb0d/compiled.gif)
- Manage Git repositories with fine grained access controls that keep your code secure - Manage Git repositories with fine grained access controls that keep your code secure
- Perform code reviews and enhance collaboration with merge requests - Perform code reviews and enhance collaboration with merge requests
...@@ -21,6 +19,8 @@ ...@@ -21,6 +19,8 @@
- [![build status](https://ci.gitlab.org/projects/1/status.png?ref=master)](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch) - [![build status](https://ci.gitlab.org/projects/1/status.png?ref=master)](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
- [![Build Status](https://semaphoreapp.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/243338/badge.png)](https://semaphoreapp.com/gitlabhq/gitlabhq)
- [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) - [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
- [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq) - [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
...@@ -29,14 +29,14 @@ ...@@ -29,14 +29,14 @@
## Website ## Website
On [www.gitlab.com](https://www.gitlab.com/) you can find more information about: On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
- [Subscriptions](https://www.gitlab.com/subscription/) - [Subscriptions](https://about.gitlab.com/subscription/)
- [Consultancy](https://www.gitlab.com/consultancy/) - [Consultancy](https://about.gitlab.com/consultancy/)
- [Community](https://www.gitlab.com/community/) - [Community](https://about.gitlab.com/community/)
- [Hosted GitLab.com](https://www.gitlab.com/gitlab-com/) use GitLab as a free service - [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service
- [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations. - [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations.
- [GitLab CI](https://www.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab. - [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
## Third-party applications ## Third-party applications
...@@ -61,11 +61,11 @@ These applications are maintained by contributors, GitLab B.V. does not offer su ...@@ -61,11 +61,11 @@ These applications are maintained by contributors, GitLab B.V. does not offer su
## Installation ## Installation
Please see [the installation page on the GitLab website](https://www.gitlab.com/installation/). Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/).
### New versions ### New versions
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://www.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
### Upgrading ### Upgrading
...@@ -85,7 +85,8 @@ Please login with `root` / `5iveL!fe` ...@@ -85,7 +85,8 @@ Please login with `root` / `5iveL!fe`
## Install a development environment ## Install a development environment
We recommend setting up your development environment with [the cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md#installation). If you do not use the cookbook you might need to copy the example development unicorn configuration file We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
If you do not use the development kit you might need to copy the example development unicorn configuration file
cp config/unicorn.rb.example.development config/unicorn.rb cp config/unicorn.rb.example.development config/unicorn.rb
...@@ -126,7 +127,7 @@ All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/ ...@@ -126,7 +127,7 @@ All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/
## Getting help ## Getting help
Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help. Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on our website for the many options to get help.
## Is it any good? ## Is it any good?
......
...@@ -43,25 +43,31 @@ ...@@ -43,25 +43,31 @@
$(".selected_issue").bind "change", Issues.checkChanged $(".selected_issue").bind "change", Issues.checkChanged
# Make sure we trigger ajax request only after user stop typing
initSearch: -> initSearch: ->
form = $("#issue_search_form") @timer = null
last_terms = ""
$("#issue_search").keyup -> $("#issue_search").keyup ->
terms = $(this).val() clearTimeout(@timer);
unless terms is last_terms @timer = setTimeout(Issues.filterResults, 500)
last_terms = terms
if terms.length >= 2 or terms.length is 0 filterResults: =>
$.ajax form = $("#issue_search_form")
type: "GET" search = $("#issue_search").val()
url: location.href $('.issues-holder').css("opacity", '0.5')
data: "issue_search=" + terms issues_url = form.attr('action') + '? '+ form.serialize()
complete: ->
$(".loading").hide() $.ajax
success: (data) -> type: "GET"
$('.issues-holder').html(data.html) url: form.attr('action')
Issues.reload() data: form.serialize()
dataType: "json" complete: ->
$('.issues-holder').css("opacity", '1.0')
success: (data) ->
$('.issues-holder').html(data.html)
# Change url so if user reload a page - search results are saved
History.replaceState {page: issues_url}, document.title, issues_url
Issues.reload()
dataType: "json"
checkChanged: -> checkChanged: ->
checked_issues = $(".selected_issue:checked") checked_issues = $(".selected_issue:checked")
......
...@@ -26,6 +26,7 @@ class Notes ...@@ -26,6 +26,7 @@ class Notes
# Reopen and close actions for Issue/MR combined with note form submit # Reopen and close actions for Issue/MR combined with note form submit
$(document).on "click", ".js-note-target-reopen", @targetReopen $(document).on "click", ".js-note-target-reopen", @targetReopen
$(document).on "click", ".js-note-target-close", @targetClose $(document).on "click", ".js-note-target-close", @targetClose
$(document).on "click", ".js-comment-button", @updateCloseButton
$(document).on "keyup", ".js-note-text", @updateTargetButtons $(document).on "keyup", ".js-note-text", @updateTargetButtons
# remove a note (in general) # remove a note (in general)
...@@ -496,6 +497,11 @@ class Notes ...@@ -496,6 +497,11 @@ class Notes
if noteText.trim().length > 0 if noteText.trim().length > 0
form.submit() form.submit()
updateCloseButton: (e) =>
textarea = $(e.target)
form = textarea.parents('form')
form.find('.js-note-target-close').text('Close')
updateTargetButtons: (e) => updateTargetButtons: (e) =>
textarea = $(e.target) textarea = $(e.target)
form = textarea.parents('form') form = textarea.parents('form')
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class @ShortcutsNavigation extends Shortcuts class @ShortcutsNavigation extends Shortcuts
constructor: -> constructor: ->
super() super()
Mousetrap.bind('g a', -> ShortcutsNavigation.findAndollowLink('.shortcuts-activity')) Mousetrap.bind('g p', -> ShortcutsNavigation.findAndollowLink('.shortcuts-project'))
Mousetrap.bind('g f', -> ShortcutsNavigation.findAndollowLink('.shortcuts-tree')) Mousetrap.bind('g f', -> ShortcutsNavigation.findAndollowLink('.shortcuts-tree'))
Mousetrap.bind('g c', -> ShortcutsNavigation.findAndollowLink('.shortcuts-commits')) Mousetrap.bind('g c', -> ShortcutsNavigation.findAndollowLink('.shortcuts-commits'))
Mousetrap.bind('g n', -> ShortcutsNavigation.findAndollowLink('.shortcuts-network')) Mousetrap.bind('g n', -> ShortcutsNavigation.findAndollowLink('.shortcuts-network'))
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
&:hover { &:hover {
background: $hover; background: $hover;
border-bottom: 1px solid #ADF; border-bottom: 1px solid darken($hover, 10%);
} }
&:last-child { &:last-child {
......
...@@ -40,7 +40,7 @@ a { ...@@ -40,7 +40,7 @@ a {
outline: none; outline: none;
color: $link_color; color: $link_color;
&:hover { &:hover {
text-decoration: none; text-decoration: underline;
color: $link_hover_color; color: $link_hover_color;
} }
...@@ -89,6 +89,8 @@ a:focus { ...@@ -89,6 +89,8 @@ a:focus {
.wiki { .wiki {
@include md-typography; @include md-typography;
word-wrap: break-word;
/* Link to current header. */ /* Link to current header. */
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
position: relative; position: relative;
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
* General Colors * General Colors
*/ */
$style_color: #474D57; $style_color: #474D57;
$hover: #D9EDF7; $hover: #FFECDB;
/* /*
* Link colors * Link colors
*/ */
$link_color: #446e9b; $link_color: #446e9b;
$link_hover_color: #2FA0BB; $link_hover_color: darken($link-color, 10%);
$btn-border: 1px solid #ccc; $btn-border: 1px solid #ccc;
......
...@@ -100,14 +100,9 @@ ...@@ -100,14 +100,9 @@
margin-right: 15px; margin-right: 15px;
font-size: 20px; font-size: 20px;
margin-bottom: 15px; margin-bottom: 15px;
border: 1px solid #EEE;
padding: 8px 12px;
border-radius: 50px;
background: #f5f5f5;
text-align: center;
i { i {
color: #BBB; color: #888;
} }
} }
......
...@@ -17,9 +17,17 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -17,9 +17,17 @@ class Projects::BranchesController < Projects::ApplicationController
end end
def create def create
@branch = CreateBranchService.new.execute(project, params[:branch_name], params[:ref], current_user) result = CreateBranchService.new.execute(project,
params[:branch_name],
redirect_to project_tree_path(@project, @branch.name) params[:ref],
current_user)
if result[:status] == :success
@branch = result[:branch]
redirect_to project_tree_path(@project, @branch.name)
else
@error = result[:message]
render action: 'new'
end
end end
def destroy def destroy
......
...@@ -31,7 +31,7 @@ class Projects::EditTreeController < Projects::BaseTreeController ...@@ -31,7 +31,7 @@ class Projects::EditTreeController < Projects::BaseTreeController
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
include_diff_info: true) include_diff_info: true)
@diff = Gitlab::DiffParser.new(diffy.diff.scan(/.*\n/)) @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
render layout: false render layout: false
end end
......
...@@ -52,7 +52,7 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -52,7 +52,7 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' } format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' }
format.js { render nothing: true } format.js
end end
end end
......
...@@ -13,10 +13,16 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -13,10 +13,16 @@ class Projects::TagsController < Projects::ApplicationController
end end
def create def create
@tag = CreateTagService.new.execute(@project, params[:tag_name], result = CreateTagService.new.execute(@project, params[:tag_name],
params[:ref], current_user) params[:ref], params[:message],
current_user)
redirect_to project_tags_path(@project) if result[:status] == :success
@tag = result[:tag]
redirect_to project_tags_path(@project)
else
@error = result[:message]
render action: 'new'
end
end end
def destroy def destroy
......
...@@ -103,7 +103,15 @@ class ProjectsController < ApplicationController ...@@ -103,7 +103,15 @@ class ProjectsController < ApplicationController
::Projects::DestroyService.new(@project, current_user, {}).execute ::Projects::DestroyService.new(@project, current_user, {}).execute
respond_to do |format| respond_to do |format|
format.html { redirect_to root_path } format.html do
flash[:alert] = "Project deleted."
if request.referer.include?("/admin")
redirect_to admin_projects_path
else
redirect_to projects_dashboard_path
end
end
end end
end end
......
...@@ -5,15 +5,23 @@ class SearchController < ApplicationController ...@@ -5,15 +5,23 @@ class SearchController < ApplicationController
@project = Project.find_by(id: params[:project_id]) if params[:project_id].present? @project = Project.find_by(id: params[:project_id]) if params[:project_id].present?
@group = Group.find_by(id: params[:group_id]) if params[:group_id].present? @group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
@scope = params[:scope] @scope = params[:scope]
@show_snippets = params[:snippets].eql? 'true'
@search_results = if @project @search_results = if @project
return access_denied! unless can?(current_user, :download_code, @project) return access_denied! unless can?(current_user, :download_code, @project)
unless %w(blobs notes issues merge_requests).include?(@scope) unless %w(blobs notes issues merge_requests wiki_blobs).
include?(@scope)
@scope = 'blobs' @scope = 'blobs'
end end
Search::ProjectService.new(@project, current_user, params).execute Search::ProjectService.new(@project, current_user, params).execute
elsif @show_snippets
unless %w(snippet_blobs snippet_titles).include?(@scope)
@scope = 'snippet_blobs'
end
Search::SnippetService.new(current_user, params).execute
else else
unless %w(projects issues merge_requests).include?(@scope) unless %w(projects issues merge_requests).include?(@scope)
@scope = 'projects' @scope = 'projects'
......
...@@ -178,6 +178,8 @@ module ApplicationHelper ...@@ -178,6 +178,8 @@ module ApplicationHelper
def search_placeholder def search_placeholder
if @project && @project.persisted? if @project && @project.persisted?
"Search in this project" "Search in this project"
elsif @snippet || @snippets || @show_snippets
'Search snippets'
elsif @group && @group.persisted? elsif @group && @group.persisted?
"Search in this group" "Search in this group"
else else
......
...@@ -16,38 +16,6 @@ module CommitsHelper ...@@ -16,38 +16,6 @@ module CommitsHelper
commit_person_link(commit, options.merge(source: :committer)) commit_person_link(commit, options.merge(source: :committer))
end end
def each_diff_line(diff, index)
Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
.each do |full_line, type, line_code, line_new, line_old|
yield(full_line, type, line_code, line_new, line_old)
end
end
def each_diff_line_near(diff, index, expected_line_code)
max_number_of_lines = 16
prev_match_line = nil
prev_lines = []
each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
line = [full_line, type, line_code, line_new, line_old]
if line_code != expected_line_code
if type == "match"
prev_lines.clear
prev_match_line = line
else
prev_lines.push(line)
prev_lines.shift if prev_lines.length >= max_number_of_lines
end
else
yield(prev_match_line) if !prev_match_line.nil?
prev_lines.each { |ln| yield(ln) }
yield(line)
break
end
end
end
def image_diff_class(diff) def image_diff_class(diff)
if diff.deleted_file if diff.deleted_file
"deleted" "deleted"
...@@ -63,14 +31,6 @@ module CommitsHelper ...@@ -63,14 +31,6 @@ module CommitsHelper
escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
end end
def diff_line_content(line)
if line.blank?
" &nbsp;"
else
line
end
end
# Breadcrumb links for a Project and, if applicable, a tree path # Breadcrumb links for a Project and, if applicable, a tree path
def commits_breadcrumbs def commits_breadcrumbs
return unless @project && @ref return unless @project && @ref
...@@ -105,82 +65,6 @@ module CommitsHelper ...@@ -105,82 +65,6 @@ module CommitsHelper
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end end
def parallel_diff_lines(project, commit, diff, file)
old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
deleted_lines = {}
added_lines = {}
each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
if type == "old"
deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
elsif type == "new"
added_lines[line_new] = { line_code: line_code, type: type, line: line }
end
end
max_length = old_file ? [old_file.loc, file.loc].max : file.loc
offset1 = 0
offset2 = 0
old_lines = []
new_lines = []
max_length.times do |line_index|
line_index1 = line_index - offset1
line_index2 = line_index - offset2
deleted_line = deleted_lines[line_index1 + 1]
added_line = added_lines[line_index2 + 1]
old_line = old_file.lines[line_index1] if old_file
new_line = file.lines[line_index2]
if deleted_line && added_line
elsif deleted_line
new_line = nil
offset2 += 1
elsif added_line
old_line = nil
offset1 += 1
end
old_lines[line_index] = DiffLine.new
new_lines[line_index] = DiffLine.new
# old
if line_index == 0 && diff.new_file
old_lines[line_index].type = :file_created
old_lines[line_index].content = 'File was created'
elsif deleted_line
old_lines[line_index].type = :deleted
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
old_lines[line_index].code = deleted_line[:line_code]
elsif old_line
old_lines[line_index].type = :no_change
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
else
old_lines[line_index].type = :added
end
# new
if line_index == 0 && diff.deleted_file
new_lines[line_index].type = :file_deleted
new_lines[line_index].content = "File was deleted"
elsif added_line
new_lines[line_index].type = :added
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
new_lines[line_index].code = added_line[:line_code]
elsif new_line
new_lines[line_index].type = :no_change
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
else
new_lines[line_index].type = :deleted
end
end
return old_lines, new_lines
end
def link_to_browse_code(project, commit) def link_to_browse_code(project, commit)
if current_controller?(:projects, :commits) if current_controller?(:projects, :commits)
if @repo.blob_at(commit.id, @path) if @repo.blob_at(commit.id, @path)
...@@ -229,14 +113,6 @@ module CommitsHelper ...@@ -229,14 +113,6 @@ module CommitsHelper
end end
end end
def diff_file_mode_changed?(diff)
diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
end
def unfold_bottom_class(bottom)
(bottom) ? 'js-unfold-bottom' : ''
end
def view_file_btn(commit_sha, diff, project) def view_file_btn(commit_sha, diff, project)
link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)), link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)),
class: 'btn btn-small view-file js-view-file' do class: 'btn btn-small view-file js-view-file' do
......
module DiffHelper module DiffHelper
def safe_diff_files(diffs) def allowed_diff_size
if diff_hard_limit_enabled? if diff_hard_limit_enabled?
diffs.first(Commit::DIFF_HARD_LIMIT_FILES) Commit::DIFF_HARD_LIMIT_FILES
else else
diffs.first(Commit::DIFF_SAFE_FILES) Commit::DIFF_SAFE_FILES
end
end
def safe_diff_files(diffs)
diffs.first(allowed_diff_size).map do |diff|
Gitlab::Diff::File.new(diff)
end end
end end
def show_diff_size_warninig?(diffs) def show_diff_size_warning?(diffs)
safe_diff_files(diffs).size < diffs.size diffs.size > allowed_diff_size
end end
def diff_hard_limit_enabled? def diff_hard_limit_enabled?
...@@ -19,4 +25,76 @@ module DiffHelper ...@@ -19,4 +25,76 @@ module DiffHelper
false false
end end
end end
def generate_line_code(file_path, line)
Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
end
def parallel_diff(diff_file, index)
lines = []
skip_next = false
# Building array of lines
#
# [left_type, left_line_number, left_line_content, line_code, right_line_type, right_line_number, right_line_content]
#
diff_file.diff_lines.each do |line|
full_line = line.text
type = line.type
line_code = generate_line_code(diff_file.file_path, line)
line_new = line.new_pos
line_old = line.old_pos
next_line = diff_file.next_line(line.index)
if next_line
next_type = next_line.type
next_line = next_line.text
end
line = [type, line_old, full_line, line_code, next_type, line_new]
if type == 'match' || type.nil?
# line in the right panel is the same as in the left one
line = [type, line_old, full_line, line_code, type, line_new, full_line]
lines.push(line)
elsif type == 'old'
if next_type == 'new'
# Left side has text removed, right side has text added
line.push(next_line)
lines.push(line)
skip_next = true
elsif next_type == 'old' || next_type.nil?
# Left side has text removed, right side doesn't have any change
line.pop # remove the newline
line.push(nil) # no line number on the right panel
line.push("&nbsp;") # empty line on the right panel
lines.push(line)
end
elsif type == 'new'
if skip_next
# Change has been already included in previous line so no need to do it again
skip_next = false
next
else
# Change is only on the right side, left side has no change
line = [nil, nil, "&nbsp;", line_code, type, line_new, full_line]
lines.push(line)
end
end
end
lines
end
def unfold_bottom_class(bottom)
(bottom) ? 'js-unfold-bottom' : ''
end
def diff_line_content(line)
if line.blank?
" &nbsp;"
else
line
end
end
end end
...@@ -178,12 +178,6 @@ module Network ...@@ -178,12 +178,6 @@ module Network
space = find_free_space(time_range, 2, space_base) space = find_free_space(time_range, 2, space_base)
leaves.each do |l| leaves.each do |l|
l.spaces << space l.spaces << space
# Also add space to parent
l.parents(@map).each do |parent|
if 0 < parent.space && parent.space < space
parent.spaces << space
end
end
end end
# and mark it as reserved # and mark it as reserved
......
...@@ -117,6 +117,25 @@ class Note < ActiveRecord::Base ...@@ -117,6 +117,25 @@ class Note < ActiveRecord::Base
}) })
end end
def create_new_commits_note(noteable, project, author, commits)
commits_text = ActionController::Base.helpers.pluralize(commits.size, 'new commit')
body = "Added #{commits_text}:\n\n"
commits.each do |commit|
message = "* #{commit.short_id} - #{commit.title}"
body << message
body << "\n"
end
create(
noteable: noteable,
project: project,
author: author,
note: body,
system: true
)
end
def discussions_from_notes(notes) def discussions_from_notes(notes)
discussion_ids = [] discussion_ids = []
discussions = [] discussions = []
...@@ -190,9 +209,10 @@ class Note < ActiveRecord::Base ...@@ -190,9 +209,10 @@ class Note < ActiveRecord::Base
noteable.diffs.each do |mr_diff| noteable.diffs.each do |mr_diff|
next unless mr_diff.new_path == self.diff.new_path next unless mr_diff.new_path == self.diff.new_path
Gitlab::DiffParser.new(mr_diff.diff.lines.to_a, mr_diff.new_path). lines = Gitlab::Diff::Parser.new.parse(mr_diff.diff.lines.to_a)
each do |full_line, type, line_code, line_new, line_old|
if full_line == diff_line lines.each do |line|
if line.text == diff_line
return true return true
end end
end end
...@@ -213,6 +233,14 @@ class Note < ActiveRecord::Base ...@@ -213,6 +233,14 @@ class Note < ActiveRecord::Base
diff.new_path if diff diff.new_path if diff
end end
def file_path
if diff.new_path.present?
diff.new_path
elsif diff.old_path.present?
diff.old_path
end
end
def diff_old_line def diff_old_line
line_code.split('_')[1].to_i line_code.split('_')[1].to_i
end end
...@@ -221,19 +249,49 @@ class Note < ActiveRecord::Base ...@@ -221,19 +249,49 @@ class Note < ActiveRecord::Base
line_code.split('_')[2].to_i line_code.split('_')[2].to_i
end end
def generate_line_code(line)
Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
end
def diff_line def diff_line
return @diff_line if @diff_line return @diff_line if @diff_line
if diff if diff
Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path) diff_lines.each do |line|
.each do |full_line, type, line_code, line_new, line_old| if generate_line_code(line) == self.line_code
@diff_line = full_line if line_code == self.line_code @diff_line = line.text
end end
end
end end
@diff_line @diff_line
end end
def truncated_diff_lines
max_number_of_lines = 16
prev_match_line = nil
prev_lines = []
diff_lines.each do |line|
if generate_line_code(line) != self.line_code
if line.type == "match"
prev_lines.clear
prev_match_line = line
else
prev_lines.push(line)
prev_lines.shift if prev_lines.length >= max_number_of_lines
end
else
prev_lines << line
return prev_lines
end
end
end
def diff_lines
@diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.lines.to_a)
end
def discussion_id def discussion_id
@discussion_id ||= Note.build_discussion_id(noteable_type, noteable_id || commit_id, line_code) @discussion_id ||= Note.build_discussion_id(noteable_type, noteable_id || commit_id, line_code)
end end
......
...@@ -70,7 +70,7 @@ class Project < ActiveRecord::Base ...@@ -70,7 +70,7 @@ class Project < ActiveRecord::Base
has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
# Merge requests from source project should be kept when source project was removed # Merge requests from source project should be kept when source project was removed
has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy has_many :issues, -> { order 'issues.state DESC, issues.created_at DESC' }, dependent: :destroy
has_many :labels, dependent: :destroy has_many :labels, dependent: :destroy
has_many :services, dependent: :destroy has_many :services, dependent: :destroy
has_many :events, dependent: :destroy has_many :events, dependent: :destroy
...@@ -400,18 +400,35 @@ class Project < ActiveRecord::Base ...@@ -400,18 +400,35 @@ class Project < ActiveRecord::Base
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/", "")
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id) commits = self.repository.commits_between(oldrev, newrev)
c_ids = commits.map(&:id)
# Close merge requests # Close merge requests
mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a
mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) }
mrs.uniq.each do |merge_request|
MergeRequests::MergeService.new.execute(merge_request, user, nil)
end
# Update code for merge requests into project between project branches # Update code for merge requests into project between project branches
mrs = self.merge_requests.opened.by_branch(branch_name).to_a mrs = self.merge_requests.opened.by_branch(branch_name).to_a
# Update code for merge requests between project and project fork # Update code for merge requests between project and project fork
mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
mrs.uniq.each do |merge_request|
merge_request.reload_code
merge_request.mark_as_unchecked
end
# Add comment about pushing new commits to merge requests
mrs = self.merge_requests.opened.where(source_branch: branch_name).to_a
mrs += self.fork_merge_requests.opened.where(source_branch: branch_name).to_a
mrs.uniq.each do |merge_request|
Note.create_new_commits_note(merge_request, merge_request.project,
user, commits)
end
true true
end end
......
...@@ -5,21 +5,17 @@ ...@@ -5,21 +5,17 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
class AssemblaService < Service class AssemblaService < Service
include HTTParty include HTTParty
prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
def title def title
......
...@@ -5,19 +5,15 @@ ...@@ -5,19 +5,15 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
class CampfireService < Service class CampfireService < Service
prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
def title def title
......
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
# Base class for CI services # Base class for CI services
......
...@@ -5,19 +5,15 @@ ...@@ -5,19 +5,15 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
class EmailsOnPushService < Service class EmailsOnPushService < Service
prop_accessor :recipients
validates :recipients, presence: true, if: :activated? validates :recipients, presence: true, if: :activated?
def title def title
......
...@@ -5,21 +5,17 @@ ...@@ -5,21 +5,17 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require "flowdock-git-hook" require "flowdock-git-hook"
class FlowdockService < Service class FlowdockService < Service
prop_accessor :token
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
def title def title
......
...@@ -5,21 +5,17 @@ ...@@ -5,21 +5,17 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require "gemnasium/gitlab_service" require "gemnasium/gitlab_service"
class GemnasiumService < Service class GemnasiumService < Service
prop_accessor :token, :api_key
validates :token, :api_key, presence: true, if: :activated? validates :token, :api_key, presence: true, if: :activated?
def title def title
......
...@@ -5,19 +5,15 @@ ...@@ -5,19 +5,15 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # property :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
class GitlabCiService < CiService class GitlabCiService < CiService
prop_accessor :project_url, :token
validates :project_url, presence: true, if: :activated? validates :project_url, presence: true, if: :activated?
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
......
...@@ -5,21 +5,17 @@ ...@@ -5,21 +5,17 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
class HipchatService < Service class HipchatService < Service
MAX_COMMITS = 3 MAX_COMMITS = 3
prop_accessor :token, :room
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
def title def title
......
...@@ -5,21 +5,17 @@ ...@@ -5,21 +5,17 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
class PivotaltrackerService < Service class PivotaltrackerService < Service
include HTTParty include HTTParty
prop_accessor :token
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
def title def title
......
...@@ -5,19 +5,15 @@ ...@@ -5,19 +5,15 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
class SlackService < Service class SlackService < Service
prop_accessor :room, :subdomain, :token
validates :room, presence: true, if: :activated? validates :room, presence: true, if: :activated?
validates :subdomain, presence: true, if: :activated? validates :subdomain, presence: true, if: :activated?
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
......
...@@ -64,10 +64,10 @@ class Repository ...@@ -64,10 +64,10 @@ class Repository
gitlab_shell.add_branch(path_with_namespace, branch_name, ref) gitlab_shell.add_branch(path_with_namespace, branch_name, ref)
end end
def add_tag(tag_name, ref) def add_tag(tag_name, ref, message = nil)
Rails.cache.delete(cache_key(:tag_names)) Rails.cache.delete(cache_key(:tag_names))
gitlab_shell.add_tag(path_with_namespace, tag_name, ref) gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
end end
def rm_branch(branch_name) def rm_branch(branch_name)
......
...@@ -5,22 +5,19 @@ ...@@ -5,22 +5,19 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
#
# To add new service you should build a class inherited from Service # To add new service you should build a class inherited from Service
# and implement a set of methods # and implement a set of methods
class Service < ActiveRecord::Base class Service < ActiveRecord::Base
serialize :properties, JSON
default_value_for :active, false default_value_for :active, false
default_value_for :properties, {}
belongs_to :project belongs_to :project
has_one :service_hook has_one :service_hook
...@@ -63,4 +60,20 @@ class Service < ActiveRecord::Base ...@@ -63,4 +60,20 @@ class Service < ActiveRecord::Base
def can_test? def can_test?
!project.empty_repo? !project.empty_repo?
end end
# Provide convenient accessor methods
# for each serialized property.
def self.prop_accessor(*args)
args.each do |arg|
class_eval %{
def #{arg}
properties['#{arg}']
end
def #{arg}=(value)
self.properties['#{arg}'] = value
end
}
end
end
end end
...@@ -65,4 +65,18 @@ class Snippet < ActiveRecord::Base ...@@ -65,4 +65,18 @@ class Snippet < ActiveRecord::Base
def expired? def expired?
expires_at && expires_at < Time.current expires_at && expires_at < Time.current
end end
class << self
def search(query)
where('(title LIKE :query OR file_name LIKE :query)', query: "%#{query}%")
end
def search_code(query)
where('(content LIKE :query)', query: "%#{query}%")
end
def accessible_to(user)
where('private = ? OR author_id = ?', false, user)
end
end
end end
class CreateBranchService class CreateBranchService
def execute(project, branch_name, ref, current_user) def execute(project, branch_name, ref, current_user)
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
if valid_branch == false
return error('Branch name invalid')
end
repository = project.repository repository = project.repository
existing_branch = repository.find_branch(branch_name)
if existing_branch
return error('Branch already exists')
end
repository.add_branch(branch_name, ref) repository.add_branch(branch_name, ref)
new_branch = repository.find_branch(branch_name) new_branch = repository.find_branch(branch_name)
if new_branch if new_branch
Event.create_ref_event(project, current_user, new_branch, 'add') Event.create_ref_event(project, current_user, new_branch, 'add')
return success(new_branch)
else
return error('Invalid reference name')
end end
end
def error(message)
{
message: message,
status: :error
}
end
new_branch def success(branch)
{
branch: branch,
status: :success
}
end end
end end
class CreateTagService class CreateTagService
def execute(project, tag_name, ref, current_user) def execute(project, tag_name, ref, message, current_user)
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
if valid_tag == false
return error('Tag name invalid')
end
repository = project.repository repository = project.repository
repository.add_tag(tag_name, ref) existing_tag = repository.find_tag(tag_name)
if existing_tag
return error('Tag already exists')
end
if message
message.gsub!(/^\s+|\s+$/, '')
end
repository.add_tag(tag_name, ref, message)
new_tag = repository.find_tag(tag_name) new_tag = repository.find_tag(tag_name)
if new_tag if new_tag
Event.create_ref_event(project, current_user, new_tag, 'add', 'refs/tags') Event.create_ref_event(project, current_user, new_tag, 'add', 'refs/tags')
return success(new_tag)
else
return error('Invalid reference name')
end end
end
def error(message)
{
message: message,
status: :error
}
end
new_tag def success(branch)
{
tag: branch,
status: :success
}
end end
end end
...@@ -5,21 +5,21 @@ class DeleteBranchService ...@@ -5,21 +5,21 @@ class DeleteBranchService
# No such branch # No such branch
unless branch unless branch
return error('No such branch') return error('No such branch', 404)
end end
if branch_name == repository.root_ref if branch_name == repository.root_ref
return error('Cannot remove HEAD branch') return error('Cannot remove HEAD branch', 405)
end end
# Dont allow remove of protected branch # Dont allow remove of protected branch
if project.protected_branch?(branch_name) if project.protected_branch?(branch_name)
return error('Protected branch cant be removed') return error('Protected branch cant be removed', 405)
end end
# Dont allow user to remove branch if he is not allowed to push # Dont allow user to remove branch if he is not allowed to push
unless current_user.can?(:push_code, project) unless current_user.can?(:push_code, project)
return error('You dont have push access to repo') return error('You dont have push access to repo', 405)
end end
if repository.rm_branch(branch_name) if repository.rm_branch(branch_name)
...@@ -30,9 +30,10 @@ class DeleteBranchService ...@@ -30,9 +30,10 @@ class DeleteBranchService
end end
end end
def error(message) def error(message, return_code = 400)
{ {
message: message, message: message,
return_code: return_code,
state: :error state: :error
} }
end end
......
module Search
class SnippetService
attr_accessor :current_user, :params
def initialize(user, params)
@current_user, @params = user, params.dup
end
def execute
snippet_ids = Snippet.accessible_to(current_user).pluck(:id)
Gitlab::SnippetSearchResults.new(snippet_ids, params[:search])
end
end
end
...@@ -70,6 +70,14 @@ ...@@ -70,6 +70,14 @@
%strong.cred %strong.cred
No No
%li
%span.light Current sign-in at:
%strong
- if @user.current_sign_in_at
= @user.current_sign_in_at.stamp("Nov 12, 2031")
- else
never
%li %li
%span.light Last sign-in at: %span.light Last sign-in at:
%strong %strong
......
%li %li
.project-access-icon %h4.project-title
= visibility_level_icon(project.visibility_level) .project-access-icon
= visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, project
.project-description - if current_page?(starred_explore_projects_path)
%h4.project-title %strong.pull-right
= link_to project.name_with_namespace, project = pluralize project.star_count, 'star'
- if current_page?(starred_explore_projects_path)
%strong.pull-right
= pluralize project.star_count, 'star'
.project-info
- if project.description.present? - if project.description.present?
%p.project-description.str-truncated %p.project-description.str-truncated
= project.description = project.description
......
...@@ -78,9 +78,9 @@ ...@@ -78,9 +78,9 @@
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key a .key p
%td %td
Go to the activity feed Go to the project's activity feed
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
- if @project && @project.persisted? - if @project && @project.persisted?
= hidden_field_tag :project_id, @project.id = hidden_field_tag :project_id, @project.id
= hidden_field_tag :search_code, true = hidden_field_tag :search_code, true
- if @snippet || @snippets
= hidden_field_tag :snippets, true
= hidden_field_tag :repository_ref, @ref = hidden_field_tag :repository_ref, @ref
= submit_tag 'Go' if ENV['RAILS_ENV'] == 'test' = submit_tag 'Go' if ENV['RAILS_ENV'] == 'test'
.search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
......
%ul.project-navigation %ul.project-navigation
= nav_link(path: 'projects#show', html_options: {class: "home"}) do = nav_link(path: 'projects#show', html_options: {class: "home"}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-activity' do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
Project Project
- if project_nav_tab? :files - if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
......
%p
Assignee changed
- if @previous_assignee
from
%strong #{@previous_assignee.name}
to
- if issuable.assignee_id
%strong #{issuable.assignee_name}
- else
%strong Unassigned
Reassigned <%= issuable.class.model_name.human.titleize %> <%= issuable.iid %>
<%= url_for([issuable.project, issuable, {only_path: false}]) %>
Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee -%>
to <%= "#{issuable.assignee_id ? issuable.assignee_name : 'Unassigned'}" %>
%p = render 'reassigned_issuable_email', issuable: @issue
Assignee changed
- if @previous_assignee
from
%strong #{@previous_assignee.name}
to
- if @issue.assignee_id
%strong #{@issue.assignee_name}
- else
%strong Unassigned
Reassigned Issue <%= @issue.iid %> <%= render 'reassigned_issuable_email', issuable: @issue %>
<%= url_for(project_issue_url(@issue.project, @issue)) %>
Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= "#{@issue.assignee_id ? @issue.assignee_name : 'Unassigned'}" %>
%p = render 'reassigned_issuable_email', issuable: @merge_request
Assignee changed
- if @previous_assignee
from
%strong #{@previous_assignee.name}
to
%strong #{@merge_request.assignee_name}
Reassigned Merge Request #<%= @merge_request.iid %> <%= render 'reassigned_issuable_email', issuable: @merge_request %>
<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= @merge_request.assignee_name %>
.form-group
= f.label :title, class: 'control-label' do
%strong= 'Title *'
.col-sm-10
= f.text_field :title, maxlength: 255, autofocus: true,
class: 'form-control pad js-gfm-input', required: true
.form-group
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
= f.text_area :description, rows: 14,
class: 'form-control js-gfm-input markdown-area'
.col-sm-12.hint
.pull-left
Parsed with
#{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}.
.pull-right
Attach images (JPG, PNG, GIF) by dragging &amp; dropping
or #{link_to 'selecting them', '#', class: 'markdown-selector' }.
.clearfix
.error-alert
%hr
.form-group
.issue-assignee
= f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
.col-sm-10
= project_users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
placeholder: 'Select a user', class: 'custom-form-control',
selected: issuable.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(issuable),
{ include_blank: 'Select milestone' }, { class: 'select2' })
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
- 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 @ref != @commit.sha
= link_to 'permalink', project_blob_path(@project,
tree_join(@commit.sha, @path)), class: 'btn btn-small'
- if allowed_tree_edit? - if allowed_tree_edit?
= link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do = link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do
......
- if @lines.present? - if @lines.present?
- if @form.unfold? && @form.since != 1 && !@form.bottom? - if @form.unfold? && @form.since != 1 && !@form.bottom?
%tr.line_holder{ id: @form.since } %tr.line_holder{ id: @form.since }
= render "projects/commits/diffs/match_line", {line: @match_line, = render "projects/diffs/match_line", {line: @match_line,
line_old: @form.since, line_new: @form.since, bottom: false} line_old: @form.since, line_new: @form.since, bottom: false}
- @lines.each_with_index do |line, index| - @lines.each_with_index do |line, index|
...@@ -15,5 +15,5 @@ ...@@ -15,5 +15,5 @@
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc - if @form.unfold? && @form.bottom? && @form.to < @blob.loc
%tr.line_holder{ id: @form.to } %tr.line_holder{ id: @form.to }
= render "projects/commits/diffs/match_line", {line: @match_line, = render "projects/diffs/match_line", {line: @match_line,
line_old: @form.to, line_new: @form.to, bottom: true} line_old: @form.to, line_new: @form.to, bottom: true}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%li(class="js-branch-#{branch.name}") %li(class="js-branch-#{branch.name}")
%h4 %h4
= link_to project_tree_path(@project, branch.name) do = link_to project_tree_path(@project, branch.name) do
%strong= truncate(branch.name, length: 60) %strong.str-truncated= branch.name
- if branch.name == @repository.root_ref - if branch.name == @repository.root_ref
%span.label.label-info default %span.label.label-info default
- if @project.protected_branch? branch.name - if @project.protected_branch? branch.name
......
- if @error
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
= @error
%h3.page-title %h3.page-title
%i.icon-code-fork %i.icon-code-fork
New branch New branch
...@@ -5,11 +9,11 @@ ...@@ -5,11 +9,11 @@
.form-group .form-group
= label_tag :branch_name, 'Name for new branch', class: 'control-label' = label_tag :branch_name, 'Name for new branch', class: 'control-label'
.col-sm-10 .col-sm-10
= text_field_tag :branch_name, nil, placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control' = text_field_tag :branch_name, params[:branch_name], placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control'
.form-group .form-group
= label_tag :ref, 'Create from', class: 'control-label' = label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10 .col-sm-10
= text_field_tag :ref, nil, placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control'
.form-actions .form-actions
= submit_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = submit_tag 'Create branch', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel'
......
= render "commit_box" = render "commit_box"
= render "projects/commits/diffs", diffs: @diffs, project: @project = render "projects/diffs/diffs", diffs: @diffs, project: @project
= render "projects/notes/notes_with_form" = render "projects/notes/notes_with_form"
/ Side-by-side diff view
- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- num_lines = old_lines.length
%div.text-file
%table
- num_lines.times do |index|
- new_line = new_lines[index]
- old_line = old_lines[index]
%tr.line_holder.parallel
-# For old line
- if old_line.type == :file_created
%td.old_line= old_line.num
%td.line_content.parallel= "File was created"
- elsif old_line.type == :deleted
%td.old_line.old= old_line.num
%td.line_content{class: "parallel noteable_line old #{old_line.code}", "line_code" => old_line.code}= old_line.content
- else old_line.type == :no_change
%td.old_line= old_line.num
%td.line_content.parallel= old_line.content
-# For new line
- if new_line.type == :file_deleted
%td.new_line= new_line.num
%td.line_content.parallel= "File was deleted"
- elsif new_line.type == :added
%td.new_line.new= new_line.num
%td.line_content{class: "parallel noteable_line new #{new_line.code}", "line_code" => new_line.code}= new_line.content
- else new_line.type == :no_change
%td.new_line= new_line.num
%td.line_content.parallel= new_line.content
- if @reply_allowed
- comments1 = @line_notes.select { |n| n.line_code == old_line.code }.sort_by(&:created_at)
- comments2 = @line_notes.select { |n| n.line_code == new_line.code }.sort_by(&:created_at)
- unless comments1.empty? and comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
- else - else
%ul.well-list= render Commit.decorate(@commits), project: @project %ul.well-list= render Commit.decorate(@commits), project: @project
= render "projects/commits/diffs", diffs: @diffs, project: @project = render "projects/diffs/diffs", diffs: @diffs, project: @project
- else - else
.light-well .light-well
......
.row .row
.col-md-8 .col-md-8
= render 'projects/commits/diff_stats', diffs: diffs = render 'projects/diffs/stats', diffs: diffs
.col-md-4 .col-md-4
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li.pull-right{class: params[:view] == 'parallel' ? 'active' : ''} %li.pull-right{class: params[:view] == 'parallel' ? 'active' : ''}
...@@ -11,16 +11,17 @@ ...@@ -11,16 +11,17 @@
- params_copy[:view] = 'inline' - params_copy[:view] = 'inline'
= link_to "Inline Diff", url_for(params_copy), {id: "commit-diff-viewtype"} = link_to "Inline Diff", url_for(params_copy), {id: "commit-diff-viewtype"}
- if show_diff_size_warninig?(diffs)
= render 'projects/commits/diff_warning', diffs: diffs - if show_diff_size_warning?(diffs)
= render 'projects/diffs/warning', diffs: diffs
.files .files
- safe_diff_files(diffs).each_with_index do |diff, i| - safe_diff_files(diffs).each_with_index do |diff_file, index|
= render 'projects/commits/diff_file', diff: diff, i: i, project: project = render 'projects/diffs/file', diff_file: diff_file, i: index, project: project
- if @diff_timeout - if @diff_timeout
.alert.alert-danger .alert.alert-danger
%h4 %h4
Failed to collect changes Failed to collect changes
%p %p
Maybe diff is really big and operation failed with timeout. Try to get diff localy Maybe diff is really big and operation failed with timeout. Try to get diff locally
- file = project.repository.blob_for_diff(@commit, diff) - blob = project.repository.blob_for_diff(@commit, diff_file.diff)
- return unless file - return unless blob
- blob_diff_path = diff_project_blob_path(project, - blob_diff_path = diff_project_blob_path(project, tree_join(@commit.id, diff_file.file_path))
tree_join(@commit.id, diff.new_path))
.diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }} .diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }}
.diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"} .diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"}
- if diff.deleted_file - if diff_file.deleted_file
%span= diff.old_path %span= diff_file.old_path
.diff-btn-group .diff-btn-group
- if @commit.parent_ids.present? - if @commit.parent_ids.present?
= view_file_btn(@commit.parent_id, diff, project) = view_file_btn(@commit.parent_id, diff_file, project)
- else - else
%span= diff.new_path %span= diff_file.new_path
- if diff_file_mode_changed?(diff) - if diff_file.mode_changed?
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}" %span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
.diff-btn-group .diff-btn-group
%label - unless params[:view] == 'parallel'
= check_box_tag nil, 1, false, class: "js-toggle-diff-line-wrap" %label
Wrap text = check_box_tag nil, 1, false, class: "js-toggle-diff-line-wrap"
Wrap text
&nbsp; &nbsp;
= link_to "#", class: "js-toggle-diff-comments btn btn-small" do = link_to "#", class: "js-toggle-diff-comments btn btn-small" do
%i.icon-chevron-down %i.icon-chevron-down
...@@ -26,23 +26,23 @@ ...@@ -26,23 +26,23 @@
&nbsp; &nbsp;
- if @merge_request && @merge_request.source_project - if @merge_request && @merge_request.source_project
= link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do = link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff_file.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do
Edit Edit
&nbsp; &nbsp;
= view_file_btn(@commit.id, diff, project) = view_file_btn(@commit.id, diff_file, project)
.diff-content .diff-content
-# Skipp all non non-supported blobs -# Skipp all non non-supported blobs
- return unless file.respond_to?('text?') - return unless blob.respond_to?('text?')
- if file.text? - if blob.text?
- if params[:view] == 'parallel' - if params[:view] == 'parallel'
= render "projects/commits/parallel_view", diff: diff, project: project, file: file, index: i = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
- else - else
= render "projects/commits/text_file", diff: diff, index: i = render "projects/diffs/text_file", diff_file: diff_file, index: i
- elsif file.image? - elsif blob.image?
- old_file = project.repository.prev_blob_for_diff(@commit, diff) - old_file = project.repository.prev_blob_for_diff(@commit, diff_file)
= render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i
- else - else
.nothing-here-block No preview for this file type .nothing-here-block No preview for this file type
- diff = diff_file.diff
- if diff.renamed_file || diff.new_file || diff.deleted_file - if diff.renamed_file || diff.new_file || diff.deleted_file
.image .image
%span.wrap %span.wrap
......
%td.old_line
%td.line_content.parallel.matched= line
%td.new_line
%td.line_content.parallel.matched= line
/ Side-by-side diff view
%div.text-file.diff-wrap-lines
%table
- parallel_diff(diff_file, index).each do |line|
- type_left = line[0]
- line_number_left = line[1]
- line_content_left = line[2]
- line_code = line[3]
- type_right = line[4]
- line_number_right = line[5]
- line_content_right = line[6]
%tr.line_holder.parallel{id: line_code}
- if type_left == 'match'
= render "projects/diffs/match_line_parallel", { line: line_content_left,
line_old: line_number_left, line_new: line_number_right }
- elsif type_left == 'old' || type_left.nil?
%td.old_line{class: "#{type_left}"}
= link_to raw(line_number_left), "##{line_code}", id: line_code
%td.line_content{class: "parallel noteable_line #{type_left} #{line_code}", "line_code" => line_code }= raw line_content_left
%td.new_line{ class: "#{type_right == 'new' ? 'new' : nil}", data: { linenumber: line_number_right }}
= link_to raw(line_number_right), "##{line_code}", id: line_code
%td.line_content.parallel{class: "noteable_line #{type_right == 'new' ? 'new' : nil} #{line_code}", "line_code" => line_code}= raw line_content_right
- if diff_file.diff.diff.blank? && diff_file.mode_changed?
.file-mode-changed
File mode changed
- too_big = diff.diff.lines.count > Commit::DIFF_SAFE_LINES - too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES
- if too_big - if too_big
%a.supp_diff_link Changes suppressed. Click to show %a.supp_diff_link Changes suppressed. Click to show
%table.text-file{class: "#{'hide' if too_big}"} %table.text-file{class: "#{'hide' if too_big}"}
- last_line = 0 - last_line = 0
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| - diff_file.diff_lines.each_with_index do |line, index|
- last_line = line_new - type = line.type
- last_line = line.new_pos
- line_code = generate_line_code(diff_file.file_path, line)
- line_old = line.old_pos
%tr.line_holder{ id: line_code, class: "#{type}" } %tr.line_holder{ id: line_code, class: "#{type}" }
- if type == "match" - if type == "match"
= render "projects/commits/diffs/match_line", {line: line, = render "projects/diffs/match_line", {line: line.text,
line_old: line_old, line_new: line_new, bottom: false} line_old: line_old, line_new: line.new_pos, bottom: false}
- else - else
%td.old_line %td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
- if @comments_allowed - if @comments_allowed
= link_to_new_diff_note(line_code) = link_to_new_diff_note(line_code)
%td.new_line{data: {linenumber: line_new}} %td.new_line{data: {linenumber: line.new_pos}}
= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code = link_to raw(type == "old" ? "&nbsp;" : line.new_pos) , "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text)
- if @reply_allowed - if @reply_allowed
- comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
- unless comments.empty? - unless comments.empty?
= render "projects/notes/diff_notes_with_reply", notes: comments, line: line = render "projects/notes/diff_notes_with_reply", notes: comments, line: line.text
- if last_line > 0 - if last_line > 0
= render "projects/commits/diffs/match_line", {line: "", = render "projects/diffs/match_line", {line: "",
line_old: last_line, line_new: last_line, bottom: true} line_old: last_line, line_new: last_line, bottom: true}
- if diff.diff.blank? && diff_file_mode_changed?(diff) - if diff_file.diff.blank? && diff_file.mode_changed?
.file-mode-changed .file-mode-changed
File mode changed File mode changed
...@@ -14,6 +14,6 @@ ...@@ -14,6 +14,6 @@
= link_to "Email patch", project_merge_request_path(@project, @merge_request, format: :patch), class: "btn btn-warning btn-small" = link_to "Email patch", project_merge_request_path(@project, @merge_request, format: :patch), class: "btn btn-warning btn-small"
%p %p
To preserve performance only To preserve performance only
%strong #{safe_diff_files(diffs).size} of #{diffs.size} %strong #{allowed_diff_size} of #{diffs.size}
files displayed. files displayed.
%table.text-file
- each_diff_line(diff, 1) do |line, type, line_code, line_new, line_old, raw_line|
%tr.line_holder{ id: line_code, class: "#{type}" }
- if type == "match"
%td.old_line= "..."
%td.new_line= "..."
%td.line_content.matched= line
- else
%td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
...@@ -9,18 +9,17 @@ ...@@ -9,18 +9,17 @@
= raw render_markup(@blob.name, @content) = raw render_markup(@blob.name, @content)
- else - else
.file-content.code .file-content.code
- unless @diff.empty? - unless @diff_lines.empty?
%table.text-file %table.text-file
- @diff.each do |line, type, line_code, line_new, line_old, raw_line| - @diff_lines.each do |line|
%tr.line_holder{ id: line_code, class: "#{type}" } %tr.line_holder{ class: "#{line.type}" }
- if type == "match" - if line.type == "match"
%td.old_line= "..." %td.old_line= "..."
%td.new_line= "..." %td.new_line= "..."
%td.line_content.matched= line %td.line_content.matched= line.text
- else - else
%td.old_line %td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code %td.new_line
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code %td.line_content{class: "#{line.type}"}= raw diff_line_content(line.text)
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
- else - else
.nothing-here-block No changes. .nothing-here-block No changes.
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
.col-sm-10 .col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br %br
The import will time out after 4 minutes. For big repositories, use a clone/push combination. The import will time out after 4 minutes. For big repositories, use a clone/push combination.
.form-actions .form-actions
......
...@@ -16,37 +16,7 @@ ...@@ -16,37 +16,7 @@
- @issue.errors.full_messages.each do |msg| - @issue.errors.full_messages.each do |msg|
%span= msg %span= msg
%br %br
.form-group = render 'projects/issuable_form', f: f, issuable: @issue
= f.label :title, class: 'control-label' do
%strong= 'Title *'
.col-sm-10
= f.text_field :title, maxlength: 255, class: "form-control js-gfm-input", autofocus: true, required: true
.form-group
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
= f.text_area :description, class: 'form-control js-gfm-input markdown-area', rows: 14
.col-sm-12.hint
.pull-left Issues are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
.pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.clearfix
.error-alert
%hr
.form-group
.issue-assignee
= f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
.col-sm-10
= project_users_select_tag('issue[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @issue.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2'})
.form-group .form-group
= f.label :label_ids, class: 'control-label' do = f.label :label_ids, class: 'control-label' do
%i.icon-tag %i.icon-tag
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
%i.icon.icon-list %i.icon.icon-list
= form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
.append-right-10.hidden-xs.hidden-sm .append-right-10.hidden-xs.hidden-sm
= search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
= hidden_field_tag :state, params['state'] = hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope'] = hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id'] = hidden_field_tag :assignee_id, params['assignee_id']
......
- if @project.labels.size == 0
$('.labels').load(document.URL + ' .light-well').hide().fadeIn(1000)
...@@ -7,11 +7,11 @@ ...@@ -7,11 +7,11 @@
Labels Labels
%hr %hr
- if @labels.present? .labels
%ul.bordered-list.manage-labels-list - if @labels.present?
= render @labels %ul.bordered-list.manage-labels-list
= paginate @labels, theme: 'gitlab' = render @labels
= paginate @labels, theme: 'gitlab'
- else - else
.light-well .light-well
.nothing-here-block Create first label or #{link_to 'generate', generate_project_labels_path(@project), method: :post} default set of labels .nothing-here-block Create first label or #{link_to 'generate', generate_project_labels_path(@project), method: :post} default set of labels
...@@ -15,37 +15,7 @@ ...@@ -15,37 +15,7 @@
%div= msg %div= msg
.merge-request-form-info .merge-request-form-info
.form-group = render 'projects/issuable_form', f: f, issuable: @merge_request
= f.label :title, class: 'control-label' do
%strong= "Title *"
.col-sm-10= f.text_field :title, class: "form-control pad js-gfm-input", maxlength: 255, rows: 5, required: true
.form-group
= f.label :description, "Description", class: 'control-label'
.col-sm-10
= f.text_area :description, class: "form-control js-gfm-input markdown-area", rows: 14
.col-sm-12.hint
.pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
.pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.clearfix
.error-alert
%hr
.form-group
.issue-assignee
= f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
.col-sm-10
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.form-group .form-group
= f.label :label_ids, class: 'control-label' do = f.label :label_ids, class: 'control-label' do
%i.icon-tag %i.icon-tag
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
%h4 Changes %h4 Changes
- if @diffs.present? - if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project = render "projects/diffs/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger .bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
......
- if @merge_request_diff.collected? - if @merge_request_diff.collected?
= render "projects/commits/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project
- elsif @merge_request_diff.empty? - elsif @merge_request_diff.empty?
.nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
- else - else
......
...@@ -48,9 +48,9 @@ ...@@ -48,9 +48,9 @@
.col-sm-10 .col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br %br
The import will time out after 2 minutes. For big repositories, use a clone/push combination. The import will time out after 4 minutes. For big repositories, use a clone/push combination.
%hr %hr
.form-group .form-group
......
- note = @project.notes.new(@comments_target.merge({ line_code: line_code }))
= link_to "",
"javascript:;",
class: "add-diff-note js-add-diff-note-button",
data: { noteable_type: note.noteable_type,
noteable_id: note.noteable_id,
commit_id: note.commit_id,
line_code: note.line_code,
discussion_id: note.discussion_id },
title: "Add a comment to this line"
...@@ -11,16 +11,17 @@ ...@@ -11,16 +11,17 @@
%br/ %br/
.diff-content .diff-content
%table %table
- each_diff_line_near(diff, note.diff_file_index, note.line_code) do |line, type, line_code, line_new, line_old| - note.truncated_diff_lines.each do |line|
- line_code = generate_line_code(note.file_path, line)
%tr.line_holder{ id: line_code } %tr.line_holder{ id: line_code }
- if type == "match" - if line.type == "match"
%td.old_line= "..." %td.old_line= "..."
%td.new_line= "..." %td.new_line= "..."
%td.line_content.matched= line %td.line_content.matched= line.text
- else - else
%td.old_line= raw(type == "new" ? "&nbsp;" : line_old) %td.old_line= raw(line.type == "new" ? "&nbsp;" : line.old_pos)
%td.new_line= raw(type == "old" ? "&nbsp;" : line_new) %td.new_line= raw(line.type == "old" ? "&nbsp;" : line.new_pos)
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;" %td.line_content{class: "noteable_line #{line.type} #{line_code}", "line_code" => line_code}= raw "#{line.text} &nbsp;"
- if line_code == note.line_code - if line_code == note.line_code
= render "projects/notes/diff_notes_with_reply", notes: discussion_notes = render "projects/notes/diff_notes_with_reply", notes: discussion_notes
- if @error
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
= @error
%h3.page-title %h3.page-title
%i.icon-code-fork %i.icon-code-fork
New tag New tag
...@@ -5,12 +9,17 @@ ...@@ -5,12 +9,17 @@
.form-group .form-group
= label_tag :tag_name, 'Name for new tag', class: 'control-label' = label_tag :tag_name, 'Name for new tag', class: 'control-label'
.col-sm-10 .col-sm-10
= text_field_tag :tag_name, nil, placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control' = text_field_tag :tag_name, params[:tag_name], placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control'
.form-group .form-group
= label_tag :ref, 'Create from', class: 'control-label' = label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10 .col-sm-10
= text_field_tag :ref, nil, placeholder: 'master', required: true, tabindex: 2, class: 'form-control' = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control'
.light Branch name or commit SHA .light Branch name or commit SHA
.form-group
= label_tag :message, 'Message', class: 'control-label'
.col-sm-10
= text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control'
.light (Optional) Entering a message will create an annotated tag.
.form-actions .form-actions
= submit_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = submit_tag 'Create tag', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
......
...@@ -23,3 +23,9 @@ ...@@ -23,3 +23,9 @@
Comments Comments
.pull-right .pull-right
= @search_results.notes_count = @search_results.notes_count
%li{class: ("active" if @scope == 'wiki_blobs')}
= link_to search_filter_path(scope: 'wiki_blobs') do
Wiki
.pull-right
= @search_results.wiki_blobs_count
%h4 %h4
#{@search_results.total_count} results found #{@search_results.total_count} results found
- if @project - unless @show_snippets
for #{link_to @project.name_with_namespace, @project} - if @project
- elsif @group for #{link_to @project.name_with_namespace, @project}
for #{link_to @group.name, @group} - elsif @group
for #{link_to @group.name, @group}
%hr %hr
...@@ -11,6 +12,8 @@ ...@@ -11,6 +12,8 @@
.col-sm-3 .col-sm-3
- if @project - if @project
= render "project_filter" = render "project_filter"
- elsif @show_snippets
= render 'snippet_filter'
- else - else
= render "global_filter" = render "global_filter"
.col-sm-9 .col-sm-9
......
%ul.nav.nav-pills.nav-stacked.search-filter
%li{class: ("active" if @scope == 'snippet_blobs')}
= link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do
%i.icon-code
Snippet Contents
.pull-right
= @search_results.snippet_blobs_count
%li{class: ("active" if @scope == 'snippet_titles')}
= link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do
%i.icon-book
Titles and Filenames
.pull-right
= @search_results.snippet_titles_count
.search-result-row
%span
= snippet_blob[:snippet_object].title
by
= link_to user_snippets_path(snippet_blob[:snippet_object].author) do
= image_tag avatar_icon(snippet_blob[:snippet_object].author_email), class: "avatar avatar-inline s16", alt: ''
= snippet_blob[:snippet_object].author_name
%span.light #{time_ago_with_tooltip(snippet_blob[:snippet_object].created_at)}
%h4.snippet-title
- snippet_path = reliable_snippet_path(snippet_blob[:snippet_object])
= link_to snippet_path do
.file-holder
.file-title
%i.icon-file
%strong= snippet_blob[:snippet_object].file_name
%span.options
.btn-group.tree-btn-group.pull-right
- if snippet_blob[:snippet_object].author == current_user
= link_to "Edit", edit_snippet_path(snippet_blob[:snippet_object]), class: "btn btn-tiny", title: 'Edit Snippet'
= link_to "Delete", snippet_path(snippet_blob[:snippet_object]), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-tiny", title: 'Delete Snippet'
= link_to "Raw", raw_snippet_path(snippet_blob[:snippet_object]), class: "btn btn-tiny", target: "_blank"
- if gitlab_markdown?(snippet_blob[:snippet_object].file_name)
.file-content.wiki
- snippet_blob[:snippet_chunks].each do |snippet|
- unless snippet[:data].empty?
= preserve do
= markdown(snippet[:data])
- else
.file-content.code
.nothing-here-block Empty file
- elsif markup?(snippet_blob[:snippet_object].file_name)
.file-content.wiki
- snippet_blob[:snippet_chunks].each do |snippet|
- unless snippet[:data].empty?
= render_markup(snippet_blob[:snippet_object].file_name, snippet[:data])
- else
.file-content.code
.nothing-here-block Empty file
- else
.file-content.code
%div.highlighted-data{class: user_color_scheme_class}
.line-numbers
- snippet_blob[:snippet_chunks].each do |snippet|
- unless snippet[:data].empty?
- snippet[:data].lines.to_a.size.times do |index|
- offset = defined?(snippet[:start_line]) ? snippet[:start_line] : 1
- i = index + offset
= link_to snippet_path+"#L#{i}", id: "L#{i}", rel: "#L#{i}" do
%i.icon-link
= i
- unless snippet == snippet_blob[:snippet_chunks].last
%a
= "."
.highlight.term
%pre
%code
- snippet_blob[:snippet_chunks].each do |snippet|
- unless snippet[:data].empty?
= snippet[:data]
- unless snippet == snippet_blob[:snippet_chunks].last
%a
= "..."
- else
.file-content.code
.nothing-here-block Empty file
.search-result-row
%h4.snippet-title.term
= link_to reliable_snippet_path(snippet_title) do
= truncate(snippet_title.title, length: 60)
- if snippet_title.private?
%span.label.label-gray
%i.icon-lock
private
%span.cgray.monospace.tiny.pull-right.term
= snippet_title.file_name
%small.pull-right.cgray
- if snippet_title.project_id?
= link_to snippet_title.project.name_with_namespace, project_path(snippet_title.project)
.snippet-info
= "##{snippet_title.id}"
%span
by
= link_to user_snippets_path(snippet_title.author) do
= image_tag avatar_icon(snippet_title.author_email), class: "avatar avatar-inline s16", alt: ''
= snippet_title.author_name
%span.light #{time_ago_with_tooltip(snippet_title.created_at)}
.blob-result
.file-holder
.file-title
= link_to project_wiki_path(@project, wiki_blob.filename) do
%i.icon-file
%strong
= wiki_blob.filename
.file-content.code.term
= render 'shared/file_hljs', blob: wiki_blob, first_line_number: wiki_blob.startline
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
= submit_tag 'Search', class: "btn btn-create" = submit_tag 'Search', class: "btn btn-create"
.form-group .form-group
.col-sm-2 .col-sm-2
.col-sm-10 - unless params[:snippets].eql? 'true'
= render 'filter', f: f .col-sm-10
= render 'filter', f: f
= hidden_field_tag :project_id, params[:project_id] = hidden_field_tag :project_id, params[:project_id]
= hidden_field_tag :group_id, params[:group_id] = hidden_field_tag :group_id, params[:group_id]
= hidden_field_tag :snippets, params[:snippets]
= hidden_field_tag :scope, params[:scope] = hidden_field_tag :scope, params[:scope]
.results.prepend-top-10 .results.prepend-top-10
......
#!/usr/bin/env bash #!/bin/sh
cd $(dirname $0)/.. cd $(dirname $0)/..
app_root=$(pwd) app_root=$(pwd)
...@@ -6,22 +6,22 @@ sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid" ...@@ -6,22 +6,22 @@ sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid"
sidekiq_logfile="$app_root/log/sidekiq.log" sidekiq_logfile="$app_root/log/sidekiq.log"
gitlab_user=$(ls -l config.ru | awk '{print $3}') gitlab_user=$(ls -l config.ru | awk '{print $3}')
function warn warn()
{ {
echo "$@" 1>&2 echo "$@" 1>&2
} }
function stop stop()
{ {
bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1 bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1
} }
function killall killall()
{ {
pkill -u $gitlab_user -f 'sidekiq [0-9]' pkill -u $gitlab_user -f 'sidekiq [0-9]'
} }
function restart restart()
{ {
if [ -f $sidekiq_pidfile ]; then if [ -f $sidekiq_pidfile ]; then
stop stop
...@@ -30,20 +30,20 @@ function restart ...@@ -30,20 +30,20 @@ function restart
start_sidekiq -d -L $sidekiq_logfile start_sidekiq -d -L $sidekiq_logfile
} }
function start_no_deamonize start_no_deamonize()
{ {
start_sidekiq start_sidekiq
} }
function start_sidekiq start_sidekiq()
{ {
bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
} }
function load_ok load_ok()
{ {
sidekiq_pid=$(cat $sidekiq_pidfile) sidekiq_pid=$(cat $sidekiq_pidfile)
if [[ -z $sidekiq_pid ]] ; then if [ -z "$sidekiq_pid" ] ; then
warn "Could not find a PID in $sidekiq_pidfile" warn "Could not find a PID in $sidekiq_pidfile"
exit 0 exit 0
fi fi
......
#!/usr/bin/env bash #!/bin/sh
cd $(dirname $0)/.. cd $(dirname $0)/..
app_root=$(pwd) app_root=$(pwd)
...@@ -6,28 +6,28 @@ app_root=$(pwd) ...@@ -6,28 +6,28 @@ app_root=$(pwd)
unicorn_pidfile="$app_root/tmp/pids/unicorn.pid" unicorn_pidfile="$app_root/tmp/pids/unicorn.pid"
unicorn_config="$app_root/config/unicorn.rb" unicorn_config="$app_root/config/unicorn.rb"
function get_unicorn_pid get_unicorn_pid()
{ {
local pid=$(cat $unicorn_pidfile) local pid=$(cat $unicorn_pidfile)
if [ -z $pid ] ; then if [ -z "$pid" ] ; then
echo "Could not find a PID in $unicorn_pidfile" echo "Could not find a PID in $unicorn_pidfile"
exit 1 exit 1
fi fi
unicorn_pid=$pid unicorn_pid=$pid
} }
function start start()
{ {
bundle exec unicorn_rails -D -c $unicorn_config -E $RAILS_ENV bundle exec unicorn_rails -D -c $unicorn_config -E $RAILS_ENV
} }
function stop stop()
{ {
get_unicorn_pid get_unicorn_pid
kill -QUIT $unicorn_pid kill -QUIT $unicorn_pid
} }
function reload reload()
{ {
get_unicorn_pid get_unicorn_pid
kill -USR2 $unicorn_pid kill -USR2 $unicorn_pid
......
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# How to use: # How to use:
# 1. copy file as gitlab.yml # 1. Copy file as gitlab.yml
# 2. Replace gitlab -> host with your domain # 2. Update gitlab -> host with your fully qualified domain name
# 3. Replace gitlab -> email_from # 3. Update gitlab -> email_from
# 4. If you installed Git from source, change git -> bin_path to /usr/local/bin/git
# 5. Review this configuration file for other settings you may want to adjust
production: &base production: &base
# #
...@@ -16,8 +18,8 @@ production: &base ...@@ -16,8 +18,8 @@ production: &base
gitlab: gitlab:
## Web server settings (note: host is the FQDN, do not include http://) ## Web server settings (note: host is the FQDN, do not include http://)
host: localhost host: localhost
port: 80 port: 80 # Set to 443 if using HTTPS, see installation.md#using-https for additional HTTPS configuration details
https: false https: false # Set to true if using HTTPS, see installation.md#using-https for additional HTTPS configuration details
# Uncommment this line below if your ssh host is different from HTTP/HTTPS one # Uncommment this line below if your ssh host is different from HTTP/HTTPS one
# (you'd obviously need to replace ssh.host_example.com with your own host). # (you'd obviously need to replace ssh.host_example.com with your own host).
......
...@@ -4,10 +4,10 @@ Sidekiq::Testing.inline! do ...@@ -4,10 +4,10 @@ Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do Gitlab::Seeder.quiet do
project_urls = [ project_urls = [
'https://github.com/documentcloud/underscore.git', 'https://github.com/documentcloud/underscore.git',
'https://github.com/gitlabhq/gitlabhq.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git',
'https://github.com/gitlabhq/gitlab-ci.git', 'https://gitlab.com/gitlab-org/gitlab-ci.git',
'https://github.com/gitlabhq/gitlab-shell.git', 'https://gitlab.com/gitlab-org/gitlab-shell.git',
'https://github.com/gitlabhq/testme.git', 'https://gitlab.com/gitlab-org/testme.git',
'https://github.com/twitter/flight.git', 'https://github.com/twitter/flight.git',
'https://github.com/twitter/typeahead.js.git', 'https://github.com/twitter/typeahead.js.git',
'https://github.com/h5bp/html5-boilerplate.git', 'https://github.com/h5bp/html5-boilerplate.git',
......
password = if ENV['GITLAB_ROOT_PASSWORD'].nil? || ENV['GITLAB_ROOT_PASSWORD'].empty?
"5iveL!fe"
else
ENV['GITLAB_ROOT_PASSWORD']
end
admin = User.create( admin = User.create(
email: "admin@example.com", email: "admin@example.com",
name: "Administrator", name: "Administrator",
username: 'root', username: 'root',
password: "5iveL!fe", password: password,
password_confirmation: "5iveL!fe", password_confirmation: password,
password_expires_at: Time.now, password_expires_at: Time.now,
theme_id: Gitlab::Theme::MARS theme_id: Gitlab::Theme::MARS
...@@ -19,6 +25,6 @@ puts %q[ ...@@ -19,6 +25,6 @@ puts %q[
Administrator account created: Administrator account created:
login.........root login.........root
password......5iveL!fe password......#{password}
] ]
end end
class MigrateToNewShell < ActiveRecord::Migration
def change
gitlab_shell_path = Gitlab.config.gitlab_shell.path
if system("#{gitlab_shell_path}/bin/create-hooks")
puts 'Repositories updated with new hooks'
else
raise 'Failed to rewrite gitlab-shell hooks in repositories'
end
end
end
class SerializeServiceProperties < ActiveRecord::Migration
def change
add_column :services, :properties, :text
associations =
{
AssemblaService: [:token, :subdomain],
CampfireService: [:token, :subdomain, :room],
EmailsOnPushService: [:recipients],
FlowdockService: [:token],
GemnasiumService: [:api_key, :token],
GitlabCiService: [:token, :project_url],
HipchatService: [:token, :room],
PivotaltrackerService: [:token],
SlackService: [:subdomain, :token, :room],
JenkinsService: [:token, :subdomain],
JiraService: [:project_url, :username, :password,
:api_version, :jira_issue_transition_id],
}
Service.all.each do |service|
associations[service.type.to_sym].each do |attribute|
service.send("#{attribute}=", service.attributes[attribute.to_s])
end
service.save!
end
remove_column :services, :project_url, :string
remove_column :services, :subdomain, :string
remove_column :services, :room, :string
remove_column :services, :recipients, :text
remove_column :services, :api_key, :string
remove_column :services, :token, :string
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20140730111702) do ActiveRecord::Schema.define(version: 20140907220153) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -265,16 +265,11 @@ ActiveRecord::Schema.define(version: 20140730111702) do ...@@ -265,16 +265,11 @@ ActiveRecord::Schema.define(version: 20140730111702) do
create_table "services", force: true do |t| create_table "services", force: true do |t|
t.string "type" t.string "type"
t.string "title" t.string "title"
t.string "token" t.integer "project_id", null: false
t.integer "project_id", null: false
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.boolean "active", default: false, null: false t.boolean "active", default: false, null: false
t.string "project_url" t.text "properties"
t.string "subdomain"
t.string "room"
t.text "recipients"
t.string "api_key"
end end
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
......
...@@ -196,6 +196,8 @@ Parameters: ...@@ -196,6 +196,8 @@ Parameters:
} }
``` ```
It return 200 if succeed or 400 if failed with error message explaining reason.
## Delete repository branch ## Delete repository branch
``` ```
...@@ -207,4 +209,5 @@ Parameters: ...@@ -207,4 +209,5 @@ Parameters:
- `id` (required) - The ID of a project - `id` (required) - The ID of a project
- `branch` (required) - The name of the branch - `branch` (required) - The name of the branch
It return 200 if succeed or 405 if failed with error message explaining reason. It return 200 if succeed, 404 if the branch to be deleted does not exist
or 400 for other reasons. In case of an error, an explaining message is provided.
...@@ -9,11 +9,15 @@ Get all issues created by authenticated user. This function takes pagination par ...@@ -9,11 +9,15 @@ Get all issues created by authenticated user. This function takes pagination par
GET /issues GET /issues
GET /issues?state=opened GET /issues?state=opened
GET /issues?state=closed GET /issues?state=closed
GET /issues?labels=foo
GET /issues?labels=foo,bar
GET /issues?labels=foo,bar&state=opened
``` ```
Parameters: Parameters:
- `state` (optional) - Return `all` issues or just those that are `opened` or `closed` - `state` (optional) - Return `all` issues or just those that are `opened` or `closed`
- `labels` (optional) - Comma-separated list of label names
```json ```json
[ [
...@@ -88,12 +92,16 @@ to return the list of project issues. ...@@ -88,12 +92,16 @@ to return the list of project issues.
GET /projects/:id/issues GET /projects/:id/issues
GET /projects/:id/issues?state=opened GET /projects/:id/issues?state=opened
GET /projects/:id/issues?state=closed GET /projects/:id/issues?state=closed
GET /projects/:id/issues?labels=foo
GET /projects/:id/issues?labels=foo,bar
GET /projects/:id/issues?labels=foo,bar&state=opened
``` ```
Parameters: Parameters:
- `id` (required) - The ID of a project - `id` (required) - The ID of a project
- `state` (optional) - Return `all` issues or just those that are `opened` or `closed` - `state` (optional) - Return `all` issues or just those that are `opened` or `closed`
- `labels` (optional) - Comma-separated list of label names
## Single issue ## Single issue
......
...@@ -248,6 +248,7 @@ POST /projects ...@@ -248,6 +248,7 @@ POST /projects
Parameters: Parameters:
- `name` (required) - new project name - `name` (required) - new project name
- `path` (optional) - custom repository name for new project. By default generated based on name
- `namespace_id` (optional) - namespace for the new project (defaults to user) - `namespace_id` (optional) - namespace for the new project (defaults to user)
- `description` (optional) - short project description - `description` (optional) - short project description
- `issues_enabled` (optional) - `issues_enabled` (optional)
......
...@@ -50,6 +50,7 @@ Parameters: ...@@ -50,6 +50,7 @@ Parameters:
- `id` (required) - The ID of a project - `id` (required) - The ID of a project
- `tag_name` (required) - The name of a tag - `tag_name` (required) - The name of a tag
- `ref` (required) - Create tag using commit SHA, another tag name, or branch name. - `ref` (required) - Create tag using commit SHA, another tag name, or branch name.
- `message` (optional) - Creates annotated tag.
```json ```json
[ [
...@@ -71,6 +72,9 @@ Parameters: ...@@ -71,6 +72,9 @@ Parameters:
] ]
``` ```
It returns 200 if the operation succeed. In case of an error,
405 with an explaining error message is returned.
## List repository tree ## List repository tree
Get a list of repository files and directories in a project. Get a list of repository files and directories in a project.
......
# Database Mysql # Database MySQL
## Note ## Note
...@@ -12,16 +12,16 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se ...@@ -12,16 +12,16 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Ensure you have MySQL version 5.5.14 or later # Ensure you have MySQL version 5.5.14 or later
mysql --version mysql --version
# Pick a database root password (can be anything), type it and press enter # Pick a MySQL root password (can be anything), type it and press enter
# Retype the database root password and press enter # Retype the MySQL root password and press enter
# Secure your installation. # Secure your installation
sudo mysql_secure_installation sudo mysql_secure_installation
# Login to MySQL # Login to MySQL
mysql -u root -p mysql -u root -p
# Type the database root password # Type the MySQL root password
# Create a user for GitLab # Create a user for GitLab
# do not type the 'mysql>', this is part of the prompt # do not type the 'mysql>', this is part of the prompt
......
...@@ -76,7 +76,7 @@ Is the system packaged Git too old? Remove it and compile from source. ...@@ -76,7 +76,7 @@ Is the system packaged Git too old? Remove it and compile from source.
# Install into /usr/local/bin # Install into /usr/local/bin
sudo make prefix=/usr/local install sudo make prefix=/usr/local install
# When editing config/gitlab.yml (Step 5), change the git bin_path to /usr/local/bin/git # When editing config/gitlab.yml (Step 5), change the git -> bin_path to /usr/local/bin/git
**Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 but this [has problems](https://github.com/gitlabhq/gitlabhq/issues/4866#issuecomment-32726573) while Ubuntu does not ship with one. The recommended mail server is postfix and you can install it with: **Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 but this [has problems](https://github.com/gitlabhq/gitlabhq/issues/4866#issuecomment-32726573) while Ubuntu does not ship with one. The recommended mail server is postfix and you can install it with:
...@@ -153,12 +153,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -153,12 +153,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
# Copy the example GitLab config # Copy the example GitLab config
sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml
# Make sure to change "localhost" to the fully-qualified domain name of your # Update GitLab config file, follow the directions at top of file
# host serving GitLab where necessary
#
# If you want to use https make sure that you set `https` to `true`. See #using-https for all necessary details.
#
# 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
...@@ -196,6 +191,8 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -196,6 +191,8 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
**Important Note:** Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup. **Important Note:** Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
**Note:** If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps.
### Configure GitLab DB Settings ### Configure GitLab DB Settings
# PostgreSQL only: # PostgreSQL only:
...@@ -233,17 +230,12 @@ GitLab Shell is an SSH access and repository management software developed speci ...@@ -233,17 +230,12 @@ GitLab Shell is an SSH access and repository management software developed speci
# Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed):
sudo -u git -H bundle exec rake gitlab:shell:install[v1.9.7] REDIS_URL=redis://localhost:6379 RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:shell:install[v1.9.7] REDIS_URL=redis://localhost:6379 RAILS_ENV=production
# By default, the gitlab-shell config is generated from your main gitlab config. # By default, the gitlab-shell config is generated from your main GitLab config.
#
# Note: When using GitLab with HTTPS please change the following:
# - Provide paths to the certificates under `ca_file` and `ca_path` options.
# - The `gitlab_url` option must point to the https endpoint of GitLab.
# - In case you are using self signed certificate set `self_signed_cert` to `true`.
# See #using-https for all necessary details.
#
# You can review (and modify) the gitlab-shell config as follows: # You can review (and modify) the gitlab-shell config as follows:
sudo -u git -H editor /home/git/gitlab-shell/config.yml sudo -u git -H editor /home/git/gitlab-shell/config.yml
**Note:** If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps.
### Initialize Database and Activate Advanced Features ### Initialize Database and Activate Advanced Features
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
...@@ -252,6 +244,10 @@ GitLab Shell is an SSH access and repository management software developed speci ...@@ -252,6 +244,10 @@ GitLab Shell is an SSH access and repository management software developed speci
# When done you see 'Administrator account created:' # When done you see 'Administrator account created:'
**Note:** You can set the Administrator password by supplying it in environmental variable `GITLAB_ROOT_PASSWORD`, eg.:
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=newpassword
### Install Init Script ### Install Init Script
Download the init script (will be `/etc/init.d/gitlab`): Download the init script (will be `/etc/init.d/gitlab`):
...@@ -309,14 +305,14 @@ Make sure to edit the config file to match your setup: ...@@ -309,14 +305,14 @@ Make sure to edit the config file to match your setup:
# domain name of your host serving GitLab. # domain name of your host serving GitLab.
sudo editor /etc/nginx/sites-available/gitlab sudo editor /etc/nginx/sites-available/gitlab
**Note:** If you want to use HTTPS, replace the `gitlab` Nginx config with `gitlab-ssl`. See [Using HTTPS](#using-https) for all necessary details. **Note:** If you want to use HTTPS, replace the `gitlab` Nginx config with `gitlab-ssl`. See [Using HTTPS](#using-https) for HTTPS configuration details.
### Test Configuration ### Test Configuration
Validate your `gitlab` or `gitlab-ssl` Nginx config file with the following command: Validate your `gitlab` or `gitlab-ssl` Nginx config file with the following command:
sudo nginx -t sudo nginx -t
You should receive `syntax is okay` and `test is successful` messages. If you receive errors check your `gitlab` or `gitlab-ssl` Nginx config file for typos, etc. as indiciated in the error message given. You should receive `syntax is okay` and `test is successful` messages. If you receive errors check your `gitlab` or `gitlab-ssl` Nginx config file for typos, etc. as indiciated in the error message given.
### Restart ### Restart
...@@ -350,11 +346,30 @@ Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has ...@@ -350,11 +346,30 @@ Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has
### Using HTTPS ### Using HTTPS
To recapitulate what is needed to use GitLab with HTTPS: To use GitLab with HTTPS:
1. In `gitlab.yml`:
1. Set the `port` option in section 1 to `443`.
1. Set the `https` option in section 1 to `true`.
1. In the `config.yml` of gitlab-shell:
1. Set `gitlab_url` option to the HTTPS endpoint of GitLab (e.g. `https://git.example.com`).
1. Set the certificates using either the `ca_file` or `ca_path` option.
1. Use the `gitlab-ssl` Nginx example config instead of the `gitlab` config.
1. Update `YOUR_SERVER_FQDN`.
1. Update `ssl_certificate` and `ssl_certificate_key`.
1. Review the configuration file and consider applying other security and performance enhancing features.
Using a self-signed certificate is discouraged but if you must use it follow the normal directions then:
1. In `gitlab.yml` set the `https` option to `true` 1. Generate a self-signed SSL certificate:
1. In the `config.yml` of gitlab-shell set the relevant options (see the [install GitLab Shell section](#install-gitlab-shell) of this document).
1. Use the `gitlab-ssl` nginx example config instead of the `gitlab` config. ```
mkdir -p /etc/nginx/ssl/
cd /etc/nginx/ssl/
sudo openssl req -newkey rsa:2048 -x509 -nodes -days 3560 -out gitlab.crt -keyout gitlab.key
sudo chmod o-r gitlab.key
```
1. In the `config.yml` of gitlab-shell set `self_signed_cert` to `true`.
### Additional Markup Styles ### Additional Markup Styles
...@@ -390,38 +405,4 @@ You can configure LDAP authentication in `config/gitlab.yml`. Please restart Git ...@@ -390,38 +405,4 @@ You can configure LDAP authentication in `config/gitlab.yml`. Please restart Git
### Using Custom Omniauth Providers ### Using Custom Omniauth Providers
GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships with a few providers preinstalled (e.g. LDAP, GitHub, Twitter). But sometimes that is not enough and you need to integrate with other authentication solutions. For these cases you can use the Omniauth provider. See the [omniauth integration document](doc/integration/omniauth.md)
#### Steps
These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation.
- Stop GitLab:
sudo service gitlab stop
- Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile):
gem "omniauth-your-auth-provider"
- If you're using MySQL, install the new Omniauth provider gem by running the following command:
sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment
- If you're using PostgreSQL, install the new Omniauth provider gem by running the following command:
sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment
> These are the same commands you used in the [Install Gems section](#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`.
- Start GitLab:
sudo service gitlab start
#### Examples
If you have successfully set up a provider that is not shipped with GitLab itself, please let us know.
You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations).
While we can't officially support every possible authentication mechanism out there, we'd like to at least help those with special needs.
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
- Ubuntu - Ubuntu
- Debian - Debian
- CentOS - CentOS
- RedHat Enterprise Linux - RedHat Enterprise Linux (please use the CentOS packages and instructions)
- Scientific Linux - Scientific Linux (please use the CentOS packages and instructions)
- Oracle Linux - Oracle Linux (please use the CentOS packages and instructions)
For the installations options please see [the installation page on the GitLab website](https://about.gitlab.com/installation/). For the installations options please see [the installation page on the GitLab website](https://about.gitlab.com/installation/).
......
...@@ -50,6 +50,13 @@ Before configuring individual OmniAuth providers there are a few global settings ...@@ -50,6 +50,13 @@ Before configuring individual OmniAuth providers there are a few global settings
# - { name: 'github', app_id: 'YOUR APP ID', # - { name: 'github', app_id: 'YOUR APP ID',
# app_secret: 'YOUR APP SECRET', # app_secret: 'YOUR APP SECRET',
# args: { scope: 'user:email' } } # args: { scope: 'user:email' } }
# - {"name": 'shibboleth',
# args: { shib_session_id_field: "HTTP_SHIB_SESSION_ID",
# shib_application_id_field: "HTTP_SHIB_APPLICATION_ID",
# uid_field: "HTTP_EPPN",
# name_field: "HTTP_CN",
# info_fields: {"email": "HTTP_MAIL" } } }
``` ```
1. Change `enabled` to `true`. 1. Change `enabled` to `true`.
...@@ -69,6 +76,7 @@ Before configuring individual OmniAuth providers there are a few global settings ...@@ -69,6 +76,7 @@ Before configuring individual OmniAuth providers there are a few global settings
- [GitHub](github.md) - [GitHub](github.md)
- [Google](google.md) - [Google](google.md)
- [Shibboleth](shibboleth.md)
- [Twitter](twitter.md) - [Twitter](twitter.md)
## Enable OmniAuth for an Existing User ## Enable OmniAuth for an Existing User
...@@ -82,3 +90,41 @@ Existing users can enable OmniAuth for specific providers after the account is c ...@@ -82,3 +90,41 @@ Existing users can enable OmniAuth for specific providers after the account is c
1. The user will be redirected to the provider. Once the user authorized GitLab they will be redirected back to GitLab. 1. The user will be redirected to the provider. Once the user authorized GitLab they will be redirected back to GitLab.
The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on. The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on.
## Using Custom Omniauth Providers
GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships with a few providers preinstalled (e.g. LDAP, GitHub, Twitter). But sometimes that is not enough and you need to integrate with other authentication solutions. For these cases you can use the Omniauth provider.
### Steps
These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation.
- Stop GitLab:
sudo service gitlab stop
- Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile):
gem "omniauth-your-auth-provider"
- If you're using MySQL, install the new Omniauth provider gem by running the following command:
sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment
- If you're using PostgreSQL, install the new Omniauth provider gem by running the following command:
sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment
> These are the same commands you used in the [Install Gems section](#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`.
- Start GitLab:
sudo service gitlab start
### Examples
If you have successfully set up a provider that is not shipped with GitLab itself, please let us know.
You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations).
While we can't officially support every possible authentication mechanism out there, we'd like to at least help those with specific needs.
# Shibboleth OmniAuth Provider
This documentation is for enabling shibboleth with gitlab-omnibus package.
In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure nginx that is bundled in gitlab-omnibus package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider.
To enable the Shibboleth OmniAuth provider you must:
1. Configure Apache shibboleth module. Installation and configuration of module it self is out of scope of this document.
Check https://wiki.shibboleth.net/ for more info.
1. You can find Apache config in gitlab-reciepes (https://github.com/gitlabhq/gitlab-recipes/blob/master/web-server/apache/gitlab-ssl.conf)
Following changes are needed to enable shibboleth:
protect omniauth-shibboleth callback url:
```
<Location /users/auth/shibboleth/callback>
AuthType shibboleth
ShibRequestSetting requireSession 1
ShibUseHeaders On
require valid-user
</Location>
Alias /shibboleth-sp /usr/share/shibboleth
<Location /shibboleth-sp>
Satisfy any
</Location>
<Location /Shibboleth.sso>
SetHandler shib
</Location>
```
exclude shibboleth urls from rewriting, add "RewriteCond %{REQUEST_URI} !/Shibboleth.sso" and "RewriteCond %{REQUEST_URI} !/shibboleth-sp", config should look like this:
```
#apache equivalent of nginx try files
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !/Shibboleth.sso
RewriteCond %{REQUEST_URI} !/shibboleth-sp
RewriteRule .* http://127.0.0.1:8080%{REQUEST_URI} [P,QSA]
RequestHeader set X_FORWARDED_PROTO 'https'
```
1. Edit /etc/gitlab/gitlab.rb configuration file, your shibboleth attributes should be in form of "HTTP_ATTRIBUTE" and you should addjust them to your need and environment. Add any other configuration you need.
File it should look like this:
```
external_url 'https://gitlab.example.com'
gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'
# disable nginx
nginx['enable'] = false
gitlab_rails['omniauth_allow_single_sign_on'] = true
gitlab_rails['omniauth_block_auto_created_users'] = false
gitlab_rails['omniauth_enabled'] = true
gitlab_rails['omniauth_providers'] = [
{
"name" => 'shibboleth',
"args" => {
"shib_session_id_field" => "HTTP_SHIB_SESSION_ID",
"shib_application_id_field" => "HTTP_SHIB_APPLICATION_ID",
"uid_field" => 'HTTP_EPPN',
"name_field" => 'HTTP_CN',
"info_fields" => { "email" => 'HTTP_MAIL'}
}
}
]
```
1. Save changes and reconfigure gitlab:
```
sudo gitlab-ctl reconfigure
```
On the sign in page there should now be a "Sign in with: Shibboleth" icon below the regular sign in form. Click the icon to begin the authentication process. You will be redirected to IdP server (Depends on your Shibboleth module configuration). If everything goes well the user will be returned to GitLab and will be signed in.
...@@ -115,8 +115,10 @@ Checking GitLab ... Finished ...@@ -115,8 +115,10 @@ Checking GitLab ... Finished
This will create satellite repositories for all your projects. This will create satellite repositories for all your projects.
If necessary, remove the `tmp/repo_satellites` directory and rerun the command below. If necessary, remove the `repo_satellites` directory and rerun the commands below.
``` ```
bundle exec rake gitlab:satellites:create RAILS_ENV=production sudo -u git -H mkdir -p /home/git/gitlab-satellites
sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production
sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites
``` ```
...@@ -107,11 +107,13 @@ List any major changes here, so the user is aware of them before starting to upg ...@@ -107,11 +107,13 @@ List any major changes here, so the user is aware of them before starting to upg
Check if any of these changed since last release: Check if any of these changed since last release:
- <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab>
- <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab-ssl>
- <https://gitlab.com/gitlab-org/gitlab-shell/commits/master/config.yml.example> - <https://gitlab.com/gitlab-org/gitlab-shell/commits/master/config.yml.example>
- <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/gitlab.yml.example> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/gitlab.yml.example>
- <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/unicorn.rb.example> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/unicorn.rb.example>
- <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.mysql> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.mysql>
- <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.postgresql> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.postgresql>
- <https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/rack_attack.rb.example>
#### 8. Need to update init script? #### 8. Need to update init script?
......
...@@ -10,7 +10,7 @@ GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490. ...@@ -10,7 +10,7 @@ GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490.
- Self signed SSL certificates are not supported until GitLab 5.1 - Self signed SSL certificates are not supported until GitLab 5.1
- **requires ruby1.9.3** - **requires ruby1.9.3**
## 0. Stop gitlab ## 0. Stop GitLab
sudo service gitlab stop sudo service gitlab stop
...@@ -41,7 +41,7 @@ git checkout v1.1.0 ...@@ -41,7 +41,7 @@ git checkout v1.1.0
# copy config # copy config
cp config.yml.example config.yml cp config.yml.example config.yml
# change url to gitlab instance # change url to GitLab instance
# ! make sure url end with '/' like 'https://gitlab.example/' # ! make sure url end with '/' like 'https://gitlab.example/'
vim config.yml vim config.yml
...@@ -49,14 +49,14 @@ vim config.yml ...@@ -49,14 +49,14 @@ vim config.yml
./support/rewrite-hooks.sh ./support/rewrite-hooks.sh
# check ruby version for git user ( 1.9 required!! ) # check ruby version for git user ( 1.9 required!! )
# gitlab shell requires system ruby 1.9 # GitLab shell requires system ruby 1.9
ruby -v ruby -v
# exit from git user # exit from git user
exit exit
``` ```
## 4. Copy gitlab instance to git user ## 4. Copy GitLab instance to git user
```bash ```bash
sudo cp -R /home/gitlab/gitlab /home/git/gitlab sudo cp -R /home/gitlab/gitlab /home/git/gitlab
...@@ -162,8 +162,43 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production ...@@ -162,8 +162,43 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
``` ```
**P.S. If everything works as expected you can remove gitlab user from system** ## 9. Cleanup
**If everything works as expected you can cleanup some old things**
Recommend you wait a bit and do a backup before completing the following.
```bash ```bash
# remove GitLab user from system
sudo userdel -r gitlab sudo userdel -r gitlab
cd /home/git
# cleanup .profile
## remove text from .profile added during gitolite installation:
## PATH=\$PATH:/home/git/bin
## export PATH
## to see what a clean .profile for new users on your system would look like see /etc/skel/.profile
sudo -u git -H vim .profile
# remove gitolite
sudo rm -R bin
sudo rm -Rf gitolite
sudo rm -R .gitolite
sudo rm .gitolite.rc
sudo rm -f gitlab.pub
sudo rm projects.list
# reset tmp folders
sudo service gitlab stop
cd /home/git/gitlab
sudo rm -R tmp
sudo -u git -H mkdir tmp
sudo chmod -R u+rwX tmp/
# reboot system
sudo reboot
# login, check that GitLab is running fine
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
``` ```
...@@ -135,7 +135,8 @@ git diff 6-0-stable:config/gitlab.yml.example 7-2-stable:config/gitlab.yml.examp ...@@ -135,7 +135,8 @@ git diff 6-0-stable:config/gitlab.yml.example 7-2-stable:config/gitlab.yml.examp
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/config/gitlab.yml.example but with your settings. * Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/config/unicorn.rb.example but with your settings. * Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v1.9.7/config.yml.example but with your settings. * Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v1.9.7/config.yml.example but with your settings.
* Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/lib/support/nginx/gitlab but with your settings. * HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/lib/support/nginx/gitlab-ssl but with your settings.
* Copy rack attack middleware config * Copy rack attack middleware config
```bash ```bash
......
...@@ -105,6 +105,9 @@ There are new configuration options available for gitlab.yml. View them with the ...@@ -105,6 +105,9 @@ There are new configuration options available for gitlab.yml. View them with the
git diff origin/6-9-stable:config/gitlab.yml.example origin/7-0-stable:config/gitlab.yml.example git diff origin/6-9-stable:config/gitlab.yml.example origin/7-0-stable:config/gitlab.yml.example
``` ```
* HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab-ssl but with your setting
### 7. Start application ### 7. Start application
sudo service gitlab start sudo service gitlab start
......
...@@ -89,6 +89,9 @@ There are new configuration options available for gitlab.yml. View them with the ...@@ -89,6 +89,9 @@ There are new configuration options available for gitlab.yml. View them with the
git diff 7-1-stable:config/gitlab.yml.example 7-2-stable:config/gitlab.yml.example git diff 7-1-stable:config/gitlab.yml.example 7-2-stable:config/gitlab.yml.example
``` ```
* HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab-ssl but with your setting
Update rack attack middleware config Update rack attack middleware config
``` ```
......
# From 7.2 to 7.3
# GitLab 7.3 has not been released yet!
This document currently just serves as a place to keep track of updates that will be needed for the 7.3 update.
### Update config files
* HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab-ssl but with your setting
\ No newline at end of file
...@@ -21,6 +21,9 @@ sudo -u git psql -f databasename.psql -d gitlabhq_production ...@@ -21,6 +21,9 @@ sudo -u git psql -f databasename.psql -d gitlabhq_production
# Rebuild indexes (see below) # Rebuild indexes (see below)
# Install gems for PostgreSQL (note: the line below states '--without ... mysql')
sudo -u git -H bundle install --without development test mysql --deployment
sudo service gitlab start sudo service gitlab start
``` ```
......
...@@ -15,7 +15,7 @@ Feature: Project Browse branches ...@@ -15,7 +15,7 @@ Feature: Project Browse branches
Scenario: I create a branch Scenario: I create a branch
Given I visit project branches page Given I visit project branches page
And I click new branch link And I click new branch link
When I submit new branch form And I submit new branch form
Then I should see new branch created Then I should see new branch created
@javascript @javascript
...@@ -23,3 +23,21 @@ Feature: Project Browse branches ...@@ -23,3 +23,21 @@ Feature: Project Browse branches
Given I visit project branches page Given I visit project branches page
And I click branch 'improve/awesome' delete link And I click branch 'improve/awesome' delete link
Then I should not see branch 'improve/awesome' Then I should not see branch 'improve/awesome'
Scenario: I create a branch with invalid name
Given I visit project branches page
And I click new branch link
And I submit new branch form with invalid name
Then I should see new an error that branch is invalid
Scenario: I create a branch with invalid reference
Given I visit project branches page
And I click new branch link
And I submit new branch form with invalid reference
Then I should see new an error that ref is invalid
Scenario: I create a branch that already exists
Given I visit project branches page
And I click new branch link
And I submit new branch form with branch that already exists
Then I should see new an error that branch already exists
...@@ -7,5 +7,25 @@ Feature: Project Browse tags ...@@ -7,5 +7,25 @@ Feature: Project Browse tags
Scenario: I can see all git tags Scenario: I can see all git tags
Then I should see "Shop" all tags list Then I should see "Shop" all tags list
Scenario: I create a tag
And I click new tag link
And I submit new tag form
Then I should see new tag created
Scenario: I create a tag with invalid name
And I click new tag link
And I submit new tag form with invalid name
Then I should see new an error that tag is invalid
Scenario: I create a tag with invalid reference
And I click new tag link
And I submit new tag form with invalid reference
Then I should see new an error that tag ref is invalid
Scenario: I create a tag that already exists
And I click new tag link
And I submit new tag form with tag that already exists
Then I should see new an error that tag already exists
# @wip # @wip
# Scenario: I can download project by tag # Scenario: I can download project by tag
...@@ -24,6 +24,11 @@ Feature: Project Labels ...@@ -24,6 +24,11 @@ Feature: Project Labels
When I remove label 'bug' When I remove label 'bug'
Then I should not see label 'bug' Then I should not see label 'bug'
@javascript
Scenario: I remove all labels
When I delete all labels
Then I should see labels help message
Scenario: I create a label with invalid color Scenario: I create a label with invalid color
Given I visit project "Shop" new label page Given I visit project "Shop" new label page
When I submit new label with invalid color When I submit new label with invalid color
......
...@@ -44,3 +44,9 @@ Feature: Project shortcuts ...@@ -44,3 +44,9 @@ Feature: Project shortcuts
Scenario: Navigate to wiki tab Scenario: Navigate to wiki tab
Given I press "g" and "w" Given I press "g" and "w"
Then the active main tab should be Wiki Then the active main tab should be Wiki
@javascript
Scenario: Navigate to project feed
Given I visit my project's files page
Given I press "g" and "p"
Then the active main tab should be Home
...@@ -51,3 +51,15 @@ Feature: Project Browse files ...@@ -51,3 +51,15 @@ Feature: Project Browse files
Scenario: I can browse code with Browse Code Scenario: I can browse code with Browse Code
Given I click on history link Given I click on history link
Then I see Browse code link Then I see Browse code link
# Permalink
Scenario: I click on the permalink link from a branch ref
Given I click on ".gitignore" file in repo
And I click on permalink
Then I am redirected to the permalink URL
Scenario: I don't see the permalink link from a SHA ref
Given I visit project source page for "6d394385cf567f80a8fd85055db1ab4c5295806f"
And I click on ".gitignore" file in repo
Then I don't see the permalink link
@dashboard
Feature: Snippet Search
Background:
Given I sign in as a user
And I have public "Personal snippet one" snippet
And I have private "Personal snippet private" snippet
And I have a public many lined snippet
Scenario: I should see my public and private snippets
When I search for "snippet" in snippet titles
Then I should see "Personal snippet one" in results
And I should see "Personal snippet private" in results
Scenario: I should see three surrounding lines on either side of a matching snippet line
When I search for "line seven" in snippet contents
Then I should see "line four" in results
And I should see "line seven" in results
And I should see "line ten" in results
And I should not see "line three" in results
And I should not see "line eleven" in results
...@@ -38,10 +38,38 @@ class ProjectBrowseBranches < Spinach::FeatureSteps ...@@ -38,10 +38,38 @@ class ProjectBrowseBranches < Spinach::FeatureSteps
click_button 'Create branch' click_button 'Create branch'
end end
step 'I submit new branch form with invalid name' do
fill_in 'branch_name', with: '1.0 stable'
fill_in 'ref', with: 'master'
click_button 'Create branch'
end
step 'I submit new branch form with invalid reference' do
fill_in 'branch_name', with: 'foo'
fill_in 'ref', with: 'foo'
click_button 'Create branch'
end
step 'I submit new branch form with branch that already exists' do
fill_in 'branch_name', with: 'master'
fill_in 'ref', with: 'master'
click_button 'Create branch'
end
step 'I should see new branch created' do step 'I should see new branch created' do
within '.tree-ref-holder' do page.should have_content 'deploy_keys'
page.should have_content 'deploy_keys' end
end
step 'I should see new an error that branch is invalid' do
page.should have_content 'Branch name invalid'
end
step 'I should see new an error that ref is invalid' do
page.should have_content 'Invalid reference name'
end
step 'I should see new an error that branch already exists' do
page.should have_content 'Branch already exists'
end end
step "I click branch 'improve/awesome' delete link" do step "I click branch 'improve/awesome' delete link" do
......
...@@ -90,4 +90,17 @@ class ProjectBrowseFiles < Spinach::FeatureSteps ...@@ -90,4 +90,17 @@ class ProjectBrowseFiles < Spinach::FeatureSteps
page.should_not have_link 'Browse File »' page.should_not have_link 'Browse File »'
page.should_not have_link 'Browse Dir »' page.should_not have_link 'Browse Dir »'
end end
step 'I click on permalink' do
click_link 'permalink'
end
step 'I am redirected to the permalink URL' do
expect(current_path).to eq(project_blob_path(
@project, @project.repository.commit.sha + '/.gitignore'))
end
step "I don't see the permalink link" do
expect(page).not_to have_link('permalink')
end
end end
...@@ -3,8 +3,52 @@ class ProjectBrowseTags < Spinach::FeatureSteps ...@@ -3,8 +3,52 @@ class ProjectBrowseTags < Spinach::FeatureSteps
include SharedProject include SharedProject
include SharedPaths include SharedPaths
Then 'I should see "Shop" all tags list' do step 'I should see "Shop" all tags list' do
page.should have_content "Tags" page.should have_content "Tags"
page.should have_content "v1.0.0" page.should have_content "v1.0.0"
end end
step 'I click new tag link' do
click_link 'New tag'
end
step 'I submit new tag form' do
fill_in 'tag_name', with: 'v7.0'
fill_in 'ref', with: 'master'
click_button 'Create tag'
end
step 'I submit new tag form with invalid name' do
fill_in 'tag_name', with: 'v 1.0'
fill_in 'ref', with: 'master'
click_button 'Create tag'
end
step 'I submit new tag form with invalid reference' do
fill_in 'tag_name', with: 'foo'
fill_in 'ref', with: 'foo'
click_button 'Create tag'
end
step 'I submit new tag form with tag that already exists' do
fill_in 'tag_name', with: 'v1.0.0'
fill_in 'ref', with: 'master'
click_button 'Create tag'
end
step 'I should see new tag created' do
page.should have_content 'v7.0'
end
step 'I should see new an error that tag is invalid' do
page.should have_content 'Tag name invalid'
end
step 'I should see new an error that tag ref is invalid' do
page.should have_content 'Invalid reference name'
end
step 'I should see new an error that tag already exists' do
page.should have_content 'Tag already exists'
end
end end
...@@ -74,34 +74,34 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -74,34 +74,34 @@ class ProjectIssues < Spinach::FeatureSteps
end end
Given 'I fill in issue search with "Re"' do Given 'I fill in issue search with "Re"' do
fill_in 'issue_search', with: "Re" filter_issue "Re"
end end
Given 'I fill in issue search with "Bu"' do Given 'I fill in issue search with "Bu"' do
fill_in 'issue_search', with: "Bu" filter_issue "Bu"
end end
And 'I fill in issue search with ".3"' do And 'I fill in issue search with ".3"' do
fill_in 'issue_search', with: ".3" filter_issue ".3"
end end
And 'I fill in issue search with "Something"' do And 'I fill in issue search with "Something"' do
fill_in 'issue_search', with: "Something" filter_issue "Something"
end end
And 'I fill in issue search with ""' do And 'I fill in issue search with ""' do
fill_in 'issue_search', with: "" filter_issue ""
end end
Given 'project "Shop" has milestone "v2.2"' do Given 'project "Shop" has milestone "v2.2"' do
project = Project.find_by(name: "Shop")
milestone = create(:milestone, title: "v2.2", project: project) milestone = create(:milestone, title: "v2.2", project: project)
3.times { create(:issue, project: project, milestone: milestone) } 3.times { create(:issue, project: project, milestone: milestone) }
end end
And 'project "Shop" has milestone "v3.0"' do And 'project "Shop" has milestone "v3.0"' do
project = Project.find_by(name: "Shop")
milestone = create(:milestone, title: "v3.0", project: project) milestone = create(:milestone, title: "v3.0", project: project)
3.times { create(:issue, project: project, milestone: milestone) } 3.times { create(:issue, project: project, milestone: milestone) }
...@@ -117,20 +117,20 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -117,20 +117,20 @@ class ProjectIssues < Spinach::FeatureSteps
end end
When 'I select first assignee from "Shop" project' do When 'I select first assignee from "Shop" project' do
project = Project.find_by(name: "Shop")
first_assignee = project.users.first first_assignee = project.users.first
select first_assignee.name, from: "assignee_id" select first_assignee.name, from: "assignee_id"
end end
Then 'I should see first assignee from "Shop" as selected assignee' do Then 'I should see first assignee from "Shop" as selected assignee' do
issues_assignee_selector = "#issue_assignee_id_chzn > a" issues_assignee_selector = "#issue_assignee_id_chzn > a"
project = Project.find_by(name: "Shop")
assignee_name = project.users.first.name assignee_name = project.users.first.name
page.find(issues_assignee_selector).should have_content(assignee_name) page.find(issues_assignee_selector).should have_content(assignee_name)
end end
And 'project "Shop" have "Release 0.4" open issue' do And 'project "Shop" have "Release 0.4" open issue' do
project = Project.find_by(name: "Shop")
create(:issue, create(:issue,
title: "Release 0.4", title: "Release 0.4",
project: project, project: project,
...@@ -140,7 +140,6 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -140,7 +140,6 @@ class ProjectIssues < Spinach::FeatureSteps
end end
And 'project "Shop" have "Tweet control" open issue' do And 'project "Shop" have "Tweet control" open issue' do
project = Project.find_by(name: "Shop")
create(:issue, create(:issue,
title: "Tweet control", title: "Tweet control",
project: project, project: project,
...@@ -148,7 +147,6 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -148,7 +147,6 @@ class ProjectIssues < Spinach::FeatureSteps
end end
And 'project "Shop" have "Release 0.3" closed issue' do And 'project "Shop" have "Release 0.3" closed issue' do
project = Project.find_by(name: "Shop")
create(:closed_issue, create(:closed_issue,
title: "Release 0.3", title: "Release 0.3",
project: project, project: project,
...@@ -189,25 +187,23 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -189,25 +187,23 @@ class ProjectIssues < Spinach::FeatureSteps
end end
step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do
project = Project.find_by(name: 'Shop')
issue = create(:issue, title: 'Bugfix1', description: 'Description for issue1', project: project) issue = create(:issue, title: 'Bugfix1', description: 'Description for issue1', project: project)
end end
step 'project \'Shop\' has issue \'Feature1\' with description: \'Feature submitted for issue1\'' do step 'project \'Shop\' has issue \'Feature1\' with description: \'Feature submitted for issue1\'' do
project = Project.find_by(name: 'Shop')
issue = create(:issue, title: 'Feature1', description: 'Feature submitted for issue1', project: project) issue = create(:issue, title: 'Feature1', description: 'Feature submitted for issue1', project: project)
end end
step 'I fill in issue search with \'Description for issue1\'' do step 'I fill in issue search with \'Description for issue1\'' do
fill_in 'issue_search', with: 'Description for issue' filter_issue 'Description for issue'
end end
step 'I fill in issue search with \'issue1\'' do step 'I fill in issue search with \'issue1\'' do
fill_in 'issue_search', with: 'issue1' filter_issue 'issue1'
end end
step 'I fill in issue search with \'Rock and roll\'' do step 'I fill in issue search with \'Rock and roll\'' do
fill_in 'issue_search', with: 'Description for issue' filter_issue 'Description for issue'
end end
step 'I should see \'Bugfix1\' in issues' do step 'I should see \'Bugfix1\' in issues' do
...@@ -221,4 +217,15 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -221,4 +217,15 @@ class ProjectIssues < Spinach::FeatureSteps
step 'I should not see \'Bugfix1\' in issues' do step 'I should not see \'Bugfix1\' in issues' do
page.should_not have_content 'Bugfix1' page.should_not have_content 'Bugfix1'
end end
def filter_issue(text)
fill_in 'issue_search', with: text
# make sure AJAX request finished
URI.parse(current_url).request_uri == project_issues_path(project, issue_search: text)
end
def project
@project ||= Project.find_by(name: 'Shop')
end
end end
...@@ -25,6 +25,22 @@ class ProjectLabels < Spinach::FeatureSteps ...@@ -25,6 +25,22 @@ class ProjectLabels < Spinach::FeatureSteps
end end
end end
step 'I delete all labels' do
within '.labels' do
all('.btn-remove').each do |remove|
remove.click
sleep 0.05
end
end
end
step 'I should see labels help message' do
within '.labels' do
page.should have_content 'Create first label or generate default set of '\
'labels'
end
end
step 'I submit new label \'support\'' do step 'I submit new label \'support\'' do
fill_in 'Title', with: 'support' fill_in 'Title', with: 'support'
fill_in 'Background Color', with: '#F95610' fill_in 'Background Color', with: '#F95610'
......
...@@ -269,6 +269,12 @@ module SharedPaths ...@@ -269,6 +269,12 @@ module SharedPaths
visit project_tree_path(@project, "6d39438") visit project_tree_path(@project, "6d39438")
end end
step 'I visit project source page for' \
' "6d394385cf567f80a8fd85055db1ab4c5295806f"' do
visit project_tree_path(@project,
'6d394385cf567f80a8fd85055db1ab4c5295806f')
end
step 'I visit project tags page' do step 'I visit project tags page' do
visit project_tags_path(@project) visit project_tags_path(@project)
end end
......
require_relative 'active_tab'
module SharedProjectTab module SharedProjectTab
include Spinach::DSL include Spinach::DSL
include SharedActiveTab include SharedActiveTab
......
module SharedSearch
include Spinach::DSL
def search_snippet_contents(query)
visit "/search?search=#{URI::encode(query)}&snippets=true&scope=snippet_blobs"
end
def search_snippet_titles(query)
visit "/search?search=#{URI::encode(query)}&snippets=true&scope=snippet_titles"
end
end
...@@ -18,4 +18,27 @@ module SharedSnippet ...@@ -18,4 +18,27 @@ module SharedSnippet
private: true, private: true,
author: current_user) author: current_user)
end end
And 'I have a public many lined snippet' do
create(:personal_snippet,
title: 'Many lined snippet',
content: <<-END.gsub(/^\s+\|/, ''),
|line one
|line two
|line three
|line four
|line five
|line six
|line seven
|line eight
|line nine
|line ten
|line eleven
|line twelve
|line thirteen
|line fourteen
END
file_name: 'many_lined_snippet.rb',
private: true,
author: current_user)
end
end end
class Spinach::Features::SnippetSearch < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedSnippet
include SharedUser
include SharedSearch
step 'I search for "snippet" in snippet titles' do
search_snippet_titles 'snippet'
end
step 'I search for "snippet private" in snippet titles' do
search_snippet_titles 'snippet private'
end
step 'I search for "line seven" in snippet contents' do
search_snippet_contents 'line seven'
end
step 'I should see "line seven" in results' do
page.should have_content 'line seven'
end
step 'I should see "line four" in results' do
page.should have_content 'line four'
end
step 'I should see "line ten" in results' do
page.should have_content 'line ten'
end
step 'I should not see "line eleven" in results' do
page.should_not have_content 'line eleven'
end
step 'I should not see "line three" in results' do
page.should_not have_content 'line three'
end
Then 'I should see "Personal snippet one" in results' do
page.should have_content 'Personal snippet one'
end
And 'I should see "Personal snippet private" in results' do
page.should have_content 'Personal snippet private'
end
Then 'I should not see "Personal snippet one" in results' do
page.should_not have_content 'Personal snippet one'
end
And 'I should not see "Personal snippet private" in results' do
page.should_not have_content 'Personal snippet private'
end
end
...@@ -80,9 +80,17 @@ module API ...@@ -80,9 +80,17 @@ module API
# POST /projects/:id/repository/branches # POST /projects/:id/repository/branches
post ":id/repository/branches" do post ":id/repository/branches" do
authorize_push_project authorize_push_project
@branch = CreateBranchService.new.execute(user_project, params[:branch_name], params[:ref], current_user) result = CreateBranchService.new.execute(user_project,
params[:branch_name],
present @branch, with: Entities::RepoObject, project: user_project params[:ref],
current_user)
if result[:status] == :success
present result[:branch],
with: Entities::RepoObject,
project: user_project
else
render_api_error!(result[:message], 400)
end
end end
# Delete branch # Delete branch
...@@ -99,7 +107,7 @@ module API ...@@ -99,7 +107,7 @@ module API
if result[:state] == :success if result[:state] == :success
true true
else else
render_api_error!(result[:message], 405) render_api_error!(result[:message], result[:return_code])
end end
end end
end end
......
...@@ -11,6 +11,10 @@ module API ...@@ -11,6 +11,10 @@ module API
else issues else issues
end end
end end
def filter_issues_labels(issues, labels)
issues.includes(:labels).where("labels.title" => labels.split(','))
end
end end
resource :issues do resource :issues do
...@@ -18,13 +22,22 @@ module API ...@@ -18,13 +22,22 @@ module API
# #
# Parameters: # Parameters:
# state (optional) - Return "opened" or "closed" issues # state (optional) - Return "opened" or "closed" issues
# # labels (optional) - Comma-separated list of label names
# Example Requests: # Example Requests:
# GET /issues # GET /issues
# GET /issues?state=opened # GET /issues?state=opened
# GET /issues?state=closed # GET /issues?state=closed
# GET /issues?labels=foo
# GET /issues?labels=foo,bar
# GET /issues?labels=foo,bar&state=opened
get do get do
present paginate(filter_issues_state(current_user.issues, params['state'])), with: Entities::Issue issues = current_user.issues
issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
issues = issues.order('issues.id DESC')
present paginate(issues), with: Entities::Issue
end end
end end
...@@ -34,13 +47,23 @@ module API ...@@ -34,13 +47,23 @@ module API
# Parameters: # Parameters:
# id (required) - The ID of a project # id (required) - The ID of a project
# state (optional) - Return "opened" or "closed" issues # state (optional) - Return "opened" or "closed" issues
# labels (optional) - Comma-separated list of label names
# #
# Example Requests: # Example Requests:
# GET /projects/:id/issues # GET /projects/:id/issues
# GET /projects/:id/issues?state=opened # GET /projects/:id/issues?state=opened
# GET /projects/:id/issues?state=closed # GET /projects/:id/issues?state=closed
# GET /projects/:id/issues
# GET /projects/:id/issues?labels=foo
# GET /projects/:id/issues?labels=foo,bar
# GET /projects/:id/issues?labels=foo,bar&state=opened
get ":id/issues" do get ":id/issues" do
present paginate(filter_issues_state(user_project.issues, params['state'])), with: Entities::Issue issues = user_project.issues
issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
issues = issues.order('issues.id DESC')
present paginate(issues), with: Entities::Issue
end end
# Get a single project issue # Get a single project issue
......
...@@ -32,14 +32,23 @@ module API ...@@ -32,14 +32,23 @@ module API
# id (required) - The ID of a project # id (required) - The ID of a project
# tag_name (required) - The name of the tag # tag_name (required) - The name of the tag
# ref (required) - Create tag from commit sha or branch # ref (required) - Create tag from commit sha or branch
# message (optional) - Specifying a message creates an annotated tag.
# Example Request: # Example Request:
# POST /projects/:id/repository/tags # POST /projects/:id/repository/tags
post ':id/repository/tags' do post ':id/repository/tags' do
authorize_push_project authorize_push_project
@tag = CreateTagService.new.execute(user_project, params[:tag_name], message = params[:message] || nil
params[:ref], current_user) result = CreateTagService.new.execute(user_project, params[:tag_name],
params[:ref], message,
present @tag, with: Entities::RepoObject, project: user_project current_user)
if result[:status] == :success
present result[:tag],
with: Entities::RepoObject,
project: user_project
else
render_api_error!(result[:message], 400)
end
end end
# Get a project repository tree # Get a project repository tree
......
...@@ -69,7 +69,7 @@ module Backup ...@@ -69,7 +69,7 @@ module Backup
end end
print 'Put GitLab hooks in repositories dirs'.yellow print 'Put GitLab hooks in repositories dirs'.yellow
if system("#{Gitlab.config.gitlab_shell.path}/support/rewrite-hooks.sh", Gitlab.config.gitlab_shell.repos_path) if system("#{Gitlab.config.gitlab_shell.path}/bin/create-hooks")
puts " [DONE]".green puts " [DONE]".green
else else
puts " [FAILED]".red puts " [FAILED]".red
......
...@@ -107,12 +107,17 @@ module Gitlab ...@@ -107,12 +107,17 @@ module Gitlab
# path - project path with namespace # path - project path with namespace
# tag_name - new tag name # tag_name - new tag name
# ref - HEAD for new tag # ref - HEAD for new tag
# message - optional message for tag (annotated tag)
# #
# Ex. # Ex.
# add_tag("gitlab/gitlab-ci", "v4.0", "master") # add_tag("gitlab/gitlab-ci", "v4.0", "master")
# add_tag("gitlab/gitlab-ci", "v4.0", "master", "message")
# #
def add_tag(path, tag_name, ref) def add_tag(path, tag_name, ref, message = nil)
system "#{gitlab_shell_path}/bin/gitlab-projects", "create-tag", "#{path}.git", tag_name, ref cmd = %W(#{gitlab_shell_path}/bin/gitlab-projects create-tag #{path}.git
#{tag_name} #{ref})
cmd << message unless message.nil? || message.empty?
system *cmd
end end
# Remove repository tag # Remove repository tag
......
...@@ -27,6 +27,7 @@ module Gitlab ...@@ -27,6 +27,7 @@ module Gitlab
notes notes
unsubscribes unsubscribes
all all
ci
) )
end end
end end
......
module Gitlab
module Diff
class File
attr_reader :diff
delegate :new_file, :deleted_file, :renamed_file,
:old_path, :new_path, to: :diff, prefix: false
def initialize(diff)
@diff = diff
end
# Array of Gitlab::DIff::Line objects
def diff_lines
@lines ||= parser.parse(raw_diff.lines)
end
def mode_changed?
!!(diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode)
end
def parser
Gitlab::Diff::Parser.new
end
def raw_diff
diff.diff
end
def next_line(index)
diff_lines[index + 1]
end
def prev_line(index)
if index > 0
diff_lines[index - 1]
end
end
def file_path
if diff.new_path.present?
diff.new_path
elsif diff.old_path.present?
diff.old_path
end
end
end
end
end
module Gitlab
module Diff
class Line
attr_reader :type, :text, :index, :old_pos, :new_pos
def initialize(text, type, index, old_pos, new_pos)
@text, @type, @index = text, type, index
@old_pos, @new_pos = old_pos, new_pos
end
end
end
end
module Gitlab
module Diff
class LineCode
def self.generate(file_path, new_line_position, old_line_position)
"#{Digest::SHA1.hexdigest(file_path)}_#{old_line_position}_#{new_line_position}"
end
end
end
end
module Gitlab
module Diff
class Parser
include Enumerable
def parse(lines)
@lines = lines,
lines_obj = []
line_obj_index = 0
line_old = 1
line_new = 1
type = nil
lines_arr = ::Gitlab::InlineDiff.processing lines
lines_arr.each do |line|
raw_line = line.dup
next if filename?(line)
full_line = html_escape(line.gsub(/\n/, ''))
full_line = ::Gitlab::InlineDiff.replace_markers full_line
if line.match(/^@@ -/)
type = "match"
line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
next if line_old == 1 && line_new == 1 #top of file
lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
line_obj_index += 1
next
else
type = identification_type(line)
lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
line_obj_index += 1
end
if line[0] == "+"
line_new += 1
elsif line[0] == "-"
line_old += 1
else
line_new += 1
line_old += 1
end
end
lines_obj
end
def empty?
@lines.empty?
end
private
def filename?(line)
line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
'--- /tmp/diffy', '+++ /tmp/diffy')
end
def identification_type(line)
if line[0] == "+"
"new"
elsif line[0] == "-"
"old"
else
nil
end
end
def html_escape str
replacements = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
str.gsub(/[&"'><]/, replacements)
end
end
end
end
module Gitlab
class DiffParser
include Enumerable
attr_reader :lines, :new_path
def initialize(lines, new_path = '')
@lines = lines
@new_path = new_path
end
def each
line_old = 1
line_new = 1
type = nil
lines_arr = ::Gitlab::InlineDiff.processing lines
lines_arr.each do |line|
raw_line = line.dup
next if filename?(line)
full_line = html_escape(line.gsub(/\n/, ''))
full_line = ::Gitlab::InlineDiff.replace_markers full_line
if line.match(/^@@ -/)
type = "match"
line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
next if line_old == 1 && line_new == 1 #top of file
yield(full_line, type, nil, line_new, line_old)
next
else
type = identification_type(line)
line_code = generate_line_code(new_path, line_new, line_old)
yield(full_line, type, line_code, line_new, line_old, raw_line)
end
if line[0] == "+"
line_new += 1
elsif line[0] == "-"
line_old += 1
else
line_new += 1
line_old += 1
end
end
end
def empty?
@lines.empty?
end
private
def filename?(line)
line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
'--- /tmp/diffy', '+++ /tmp/diffy')
end
def identification_type(line)
if line[0] == "+"
"new"
elsif line[0] == "-"
"old"
else
nil
end
end
def generate_line_code(path, line_new, line_old)
"#{Digest::SHA1.hexdigest(path)}_#{line_old}_#{line_new}"
end
def html_escape str
replacements = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
str.gsub(/[&"'><]/, replacements)
end
end
end
module Gitlab
module GitRefValidator
extend self
# Validates a given name against the git reference specification
#
# Returns true for a valid reference name, false otherwise
def validate(ref_name)
system *%W(git check-ref-format refs/#{ref_name})
end
end
end
...@@ -192,8 +192,12 @@ module Gitlab ...@@ -192,8 +192,12 @@ module Gitlab
link_to("##{identifier}", url, options) link_to("##{identifier}", url, options)
end end
elsif project.issues_tracker == 'jira' else
reference_jira_issue(identifier, project) config = Gitlab.config
external_issue_tracker = config.issues_tracker[project.issues_tracker]
if external_issue_tracker.present?
reference_external_issue(identifier, external_issue_tracker, project)
end
end end
end end
...@@ -229,15 +233,15 @@ module Gitlab ...@@ -229,15 +233,15 @@ module Gitlab
end end
end end
def reference_jira_issue(identifier, project = @project) def reference_external_issue(identifier, issue_tracker, project = @project)
url = url_for_issue(identifier) url = url_for_issue(identifier, project)
title = Gitlab.config.issues_tracker[project.issues_tracker]["title"] title = issue_tracker['title']
options = html_options.merge( options = html_options.merge(
title: "Issue in #{title}", title: "Issue in #{title}",
class: "gfm gfm-issue #{html_options[:class]}" class: "gfm gfm-issue #{html_options[:class]}"
) )
link_to("#{identifier}", url, options) link_to("##{identifier}", url, options)
end end
end end
end end
...@@ -14,13 +14,16 @@ module Gitlab ...@@ -14,13 +14,16 @@ module Gitlab
notes.page(page).per(per_page) notes.page(page).per(per_page)
when 'blobs' when 'blobs'
Kaminari.paginate_array(blobs).page(page).per(per_page) Kaminari.paginate_array(blobs).page(page).per(per_page)
when 'wiki_blobs'
Kaminari.paginate_array(wiki_blobs).page(page).per(per_page)
else else
super super
end end
end end
def total_count def total_count
@total_count ||= issues_count + merge_requests_count + blobs_count + notes_count @total_count ||= issues_count + merge_requests_count + blobs_count +
notes_count + wiki_blobs_count
end end
def blobs_count def blobs_count
...@@ -31,6 +34,10 @@ module Gitlab ...@@ -31,6 +34,10 @@ module Gitlab
@notes_count ||= notes.count @notes_count ||= notes.count
end end
def wiki_blobs_count
@wiki_blobs_count ||= wiki_blobs.count
end
private private
def blobs def blobs
...@@ -41,6 +48,20 @@ module Gitlab ...@@ -41,6 +48,20 @@ module Gitlab
end end
end end
def wiki_blobs
if project.wiki_enabled?
wiki_repo = Repository.new(ProjectWiki.new(project).path_with_namespace)
if wiki_repo.exists?
wiki_repo.search_files(query)
else
[]
end
else
[]
end
end
def notes def notes
Note.where(project_id: limit_project_ids).search(query).order('updated_at DESC') Note.where(project_id: limit_project_ids).search(query).order('updated_at DESC')
end end
......
module Gitlab
class SnippetSearchResults < SearchResults
attr_reader :limit_snippet_ids
def initialize(limit_snippet_ids, query)
@limit_snippet_ids = limit_snippet_ids
@query = query
end
def objects(scope, page = nil)
case scope
when 'snippet_titles'
Kaminari.paginate_array(snippet_titles).page(page).per(per_page)
when 'snippet_blobs'
Kaminari.paginate_array(snippet_blobs).page(page).per(per_page)
else
super
end
end
def total_count
@total_count ||= snippet_titles_count + snippet_blobs_count
end
def snippet_titles_count
@snippet_titles_count ||= snippet_titles.count
end
def snippet_blobs_count
@snippet_blobs_count ||= snippet_blobs.count
end
private
def snippet_titles
Snippet.where(id: limit_snippet_ids).search(query).order('updated_at DESC')
end
def snippet_blobs
search = Snippet.where(id: limit_snippet_ids).search_code(query)
search = search.order('updated_at DESC').to_a
snippets = []
search.each { |e| snippets << chunk_snippet(e) }
snippets
end
def default_scope
'snippet_blobs'
end
# Get an array of line numbers surrounding a matching
# line, bounded by min/max.
#
# @returns Array of line numbers
def bounded_line_numbers(line, min, max)
lower = line - surrounding_lines > min ? line - surrounding_lines : min
upper = line + surrounding_lines < max ? line + surrounding_lines : max
(lower..upper).to_a
end
# Returns a sorted set of lines to be included in a snippet preview.
# This ensures matching adjacent lines do not display duplicated
# surrounding code.
#
# @returns Array, unique and sorted.
def matching_lines(lined_content)
used_lines = []
lined_content.each_with_index do |line, line_number|
used_lines.concat bounded_line_numbers(
line_number,
0,
lined_content.size
) if line.include?(query)
end
used_lines.uniq.sort
end
# 'Chunkify' entire snippet. Splits the snippet data into matching lines +
# surrounding_lines() worth of unmatching lines.
#
# @returns a hash with {snippet_object, snippet_chunks:{data,start_line}}
def chunk_snippet(snippet)
lined_content = snippet.content.split("\n")
used_lines = matching_lines(lined_content)
snippet_chunk = []
snippet_chunks = []
snippet_start_line = 0
last_line = -1
# Go through each used line, and add consecutive lines as a single chunk
# to the snippet chunk array.
used_lines.each do |line_number|
if last_line < 0
# Start a new chunk.
snippet_start_line = line_number
snippet_chunk << lined_content[line_number]
elsif last_line == line_number - 1
# Consecutive line, continue chunk.
snippet_chunk << lined_content[line_number]
else
# Non-consecutive line, add chunk to chunk array.
snippet_chunks << {
data: snippet_chunk.join("\n"),
start_line: snippet_start_line + 1
}
# Start a new chunk.
snippet_chunk = [lined_content[line_number]]
snippet_start_line = line_number
end
last_line = line_number
end
# Add final chunk to chunk array
snippet_chunks << {
data: snippet_chunk.join("\n"),
start_line: snippet_start_line + 1
}
# Return snippet with chunk array
{ snippet_object: snippet, snippet_chunks: snippet_chunks }
end
# Defines how many unmatching lines should be
# included around the matching lines in a snippet
def surrounding_lines
3
end
end
end
...@@ -43,7 +43,7 @@ module Gitlab ...@@ -43,7 +43,7 @@ module Gitlab
end end
def latest_version_raw def latest_version_raw
remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags origin)) remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git))
git_tags = remote_tags.split("\n").grep(/tags\/v#{current_version.major}/) git_tags = remote_tags.split("\n").grep(/tags\/v#{current_version.major}/)
git_tags = git_tags.select { |version| version =~ /v\d\.\d\.\d\Z/ } git_tags = git_tags.select { |version| version =~ /v\d\.\d\.\d\Z/ }
last_tag = git_tags.last.match(/v\d\.\d\.\d/).to_s last_tag = git_tags.last.match(/v\d\.\d\.\d/).to_s
......
...@@ -26,23 +26,12 @@ ...@@ -26,23 +26,12 @@
## [1] https://github.com/agentzh/chunkin-nginx-module#status ## [1] https://github.com/agentzh/chunkin-nginx-module#status
## [2] https://github.com/agentzh/chunkin-nginx-module ## [2] https://github.com/agentzh/chunkin-nginx-module
## ##
###################################
## SSL file editing ##
###################################
##
## Edit `gitlab-shell/config.yml`:
## 1) Set "gitlab_url" param in `gitlab-shell/config.yml` to `https://git.example.com`
## 2) Set "ca_file" to `/etc/nginx/ssl/gitlab.crt`
## 3) Set "self_signed_cert" to `true`
## Edit `gitlab/config/gitlab.yml`:
## 1) Define port for http "port: 443"
## 2) Enable https "https: true"
## 3) Update ssl for gravatar "ssl_url: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm"
## ##
################################### ###################################
## SSL configuration ## ## SSL configuration ##
################################### ###################################
## ##
## See installation.md#using-https for additional HTTPS configuration details.
upstream gitlab { upstream gitlab {
server unix:/home/git/gitlab/tmp/sockets/gitlab.socket; server unix:/home/git/gitlab/tmp/sockets/gitlab.socket;
...@@ -76,7 +65,7 @@ server { ...@@ -76,7 +65,7 @@ server {
ssl_certificate /etc/nginx/ssl/gitlab.crt; ssl_certificate /etc/nginx/ssl/gitlab.crt;
ssl_certificate_key /etc/nginx/ssl/gitlab.key; ssl_certificate_key /etc/nginx/ssl/gitlab.key;
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4'; ssl_ciphers 'AES256+EECDH:AES256+EDH';
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache builtin:1000 shared:SSL:10m; ssl_session_cache builtin:1000 shared:SSL:10m;
...@@ -87,6 +76,23 @@ server { ...@@ -87,6 +76,23 @@ server {
add_header X-Frame-Options SAMEORIGIN; add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff; add_header X-Content-Type-Options nosniff;
## [Optional] If your certficate has OCSP, enable OCSP stapling to reduce the overhead and latency of running SSL.
## Replace with your ssl_trusted_certificate. For more info see:
## - https://medium.com/devops-programming/4445f4862461
## - https://www.ruby-forum.com/topic/4419319
## - https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx
# ssl_stapling on;
# ssl_stapling_verify on;
# ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt;
# resolver 208.67.222.222 208.67.222.220 valid=300s; # Can change to your DNS resolver if desired
# resolver_timeout 10s;
## [Optional] Generate a stronger DHE parameter:
## cd /etc/ssl/certs
## sudo openssl dhparam -out dhparam.pem 4096
##
# ssl_dhparam /etc/ssl/certs/dhparam.pem;
## Individual nginx logs for this GitLab vhost ## Individual nginx logs for this GitLab vhost
access_log /var/log/nginx/gitlab_access.log; access_log /var/log/nginx/gitlab_access.log;
error_log /var/log/nginx/gitlab_error.log; error_log /var/log/nginx/gitlab_error.log;
......
...@@ -322,7 +322,7 @@ namespace :gitlab do ...@@ -322,7 +322,7 @@ namespace :gitlab do
"core.autocrlf" => "input" "core.autocrlf" => "input"
} }
correct_options = options.map do |name, value| correct_options = options.map do |name, value|
run(%W(git config --global --get #{name})).try(:squish) == value run(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
end end
if correct_options.all? if correct_options.all?
...@@ -330,9 +330,9 @@ namespace :gitlab do ...@@ -330,9 +330,9 @@ namespace :gitlab do
else else
puts "no".red puts "no".red
try_fixing_it( try_fixing_it(
sudo_gitlab("git config --global user.name \"#{options["user.name"]}\""), sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.name \"#{options["user.name"]}\""),
sudo_gitlab("git config --global user.email \"#{options["user.email"]}\""), sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.email \"#{options["user.email"]}\""),
sudo_gitlab("git config --global core.autocrlf \"#{options["core.autocrlf"]}\"") sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"")
) )
for_more_information( for_more_information(
see_installation_guide_section "GitLab" see_installation_guide_section "GitLab"
...@@ -541,7 +541,7 @@ namespace :gitlab do ...@@ -541,7 +541,7 @@ namespace :gitlab do
"sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}"
) )
for_more_information( for_more_information(
"#{gitlab_shell_path}/support/rewrite-hooks.sh" "#{gitlab_shell_path}/bin/create-hooks"
) )
fix_and_rerun fix_and_rerun
next next
...@@ -556,7 +556,7 @@ namespace :gitlab do ...@@ -556,7 +556,7 @@ namespace :gitlab do
"sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}"
) )
for_more_information( for_more_information(
"lib/support/rewrite-hooks.sh" "#{gitlab_shell_path}/bin/create-hooks"
) )
fix_and_rerun fix_and_rerun
end end
......
...@@ -165,7 +165,6 @@ FactoryGirl.define do ...@@ -165,7 +165,6 @@ FactoryGirl.define do
factory :service do factory :service do
type "" type ""
title "GitLab CI" title "GitLab CI"
token "x56olispAND34ng"
project project
end end
......
require 'spec_helper'
describe DiffHelper do
include RepoHelpers
let(:project) { create(:project) }
let(:commit) { project.repository.commit(sample_commit.id) }
let(:diff) { commit.diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff) }
describe 'diff_hard_limit_enabled?' do
it 'should return true if param is provided' do
controller.stub(:params).and_return { { :force_show_diff => true } }
diff_hard_limit_enabled?.should be_true
end
it 'should return false if param is not provided' do
diff_hard_limit_enabled?.should be_false
end
end
describe 'allowed_diff_size' do
it 'should return hard limit for a diff if force diff is true' do
controller.stub(:params).and_return { { :force_show_diff => true } }
allowed_diff_size.should eq(1000)
end
it 'should return safe limit for a diff if force diff is false' do
allowed_diff_size.should eq(100)
end
end
describe 'parallel_diff' do
it 'should return an array of arrays containing the parsed diff' do
parallel_diff(diff_file, 0).should match_array(parallel_diff_result_array)
end
end
describe 'generate_line_code' do
it 'should generate correct line code' do
generate_line_code(diff_file.file_path, diff_file.diff_lines.first).should == '2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6'
end
end
describe 'unfold_bottom_class' do
it 'should return empty string when bottom line shouldnt be unfolded' do
unfold_bottom_class(false).should == ''
end
it 'should return js class when bottom lines should be unfolded' do
unfold_bottom_class(true).should == 'js-unfold-bottom'
end
end
describe 'diff_line_content' do
it 'should return non breaking space when line is empty' do
diff_line_content(nil).should eq(" &nbsp;")
end
it 'should return the line itself' do
diff_line_content(diff_file.diff_lines.first.text).should eq("@@ -6,12 +6,18 @@ module Popen")
diff_line_content(diff_file.diff_lines.first.type).should eq("match")
diff_line_content(diff_file.diff_lines.first.new_pos).should eq(6)
end
end
def parallel_diff_result_array
[
["match", 6, "@@ -6,12 +6,18 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6", "match", 6, "@@ -6,12 +6,18 @@ module Popen"],
[nil, 6, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6", nil, 6, " "],
[nil, 7, " def popen(cmd, path=nil)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7", nil, 7, " def popen(cmd, path=nil)"],
[nil, 8, " unless cmd.is_a?(Array)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8", nil, 8, " unless cmd.is_a?(Array)"],
["old", 9, "- raise <span class='idiff'></span>&quot;System commands must be given as an array of strings&quot;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9", "new", 9, "+ raise <span class='idiff'>RuntimeError, </span>&quot;System commands must be given as an array of strings&quot;"],
[nil, 10, " end", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10", nil, 10, " end"], [nil, 11, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_11_11", nil, 11, " "],
[nil, 12, " path ||= Dir.pwd", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_12_12", nil, 12, " path ||= Dir.pwd"],
["old", 13, "- vars = { &quot;PWD&quot; =&gt; path }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13", "old", nil, "&nbsp;"],
["old", 14, "- options = { chdir: path }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13", "new", 13, "+"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14", "new", 14, "+ vars = {"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15", "new", 15, "+ &quot;PWD&quot; =&gt; path"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_16", "new", 16, "+ }"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_17", "new", 17, "+"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_18", "new", 18, "+ options = {"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_19", "new", 19, "+ chdir: path"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_20", "new", 20, "+ }"],
[nil, 15, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_21", nil, 21, " "],
[nil, 16, " unless File.directory?(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_16_22", nil, 22, " unless File.directory?(path)"],
[nil, 17, " FileUtils.mkdir_p(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_17_23", nil, 23, " FileUtils.mkdir_p(path)"],
["match", 19, "@@ -19,6 +25,7 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25", "match", 25, "@@ -19,6 +25,7 @@ module Popen"],
[nil, 19, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25", nil, 25, " "], [nil, 20, " @cmd_output = &quot;&quot;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26", nil, 26, " @cmd_output = &quot;&quot;"],
[nil, 21, " @cmd_status = 0", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_21_27", nil, 27, " @cmd_status = 0"],
[nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_28", "new", 28, "+"],
[nil, 22, " Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_29", nil, 29, " Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|"],
[nil, 23, " @cmd_output &lt;&lt; stdout.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_23_30", nil, 30, " @cmd_output &lt;&lt; stdout.read"],
[nil, 24, " @cmd_output &lt;&lt; stderr.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_24_31", nil, 31, " @cmd_output &lt;&lt; stderr.read"]
]
end
end
require 'spec_helper'
describe Gitlab::GitRefValidator do
it { Gitlab::GitRefValidator.validate('feature/new').should be_true }
it { Gitlab::GitRefValidator.validate('implement_@all').should be_true }
it { Gitlab::GitRefValidator.validate('my_new_feature').should be_true }
it { Gitlab::GitRefValidator.validate('#1').should be_true }
it { Gitlab::GitRefValidator.validate('feature/~new/').should be_false }
it { Gitlab::GitRefValidator.validate('feature/^new/').should be_false }
it { Gitlab::GitRefValidator.validate('feature/:new/').should be_false }
it { Gitlab::GitRefValidator.validate('feature/?new/').should be_false }
it { Gitlab::GitRefValidator.validate('feature/*new/').should be_false }
it { Gitlab::GitRefValidator.validate('feature/[new/').should be_false }
it { Gitlab::GitRefValidator.validate('feature/new/').should be_false }
it { Gitlab::GitRefValidator.validate('feature/new.').should be_false }
it { Gitlab::GitRefValidator.validate('feature\@{').should be_false }
it { Gitlab::GitRefValidator.validate('feature\new').should be_false }
it { Gitlab::GitRefValidator.validate('feature//new').should be_false }
it { Gitlab::GitRefValidator.validate('feature new').should be_false }
end
require 'spec_helper'
describe Gitlab::Diff::File do
include RepoHelpers
let(:project) { create(:project) }
let(:commit) { project.repository.commit(sample_commit.id) }
let(:diff) { commit.diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff) }
describe :diff_lines do
let(:diff_lines) { diff_file.diff_lines }
it { diff_lines.size.should == 30 }
it { diff_lines.first.should be_kind_of(Gitlab::Diff::Line) }
end
describe :mode_changed? do
it { diff_file.mode_changed?.should be_false }
end
end
require 'spec_helper'
describe Gitlab::Diff::Parser do
include RepoHelpers
let(:project) { create(:project) }
let(:commit) { project.repository.commit(sample_commit.id) }
let(:diff) { commit.diffs.first }
let(:parser) { Gitlab::Diff::Parser.new }
describe :parse do
let(:diff) do
<<eos
--- a/files/ruby/popen.rb
+++ b/files/ruby/popen.rb
@@ -6,12 +6,18 @@ module Popen
def popen(cmd, path=nil)
unless cmd.is_a?(Array)
- raise "System commands must be given as an array of strings"
+ raise RuntimeError, "System commands must be given as an array of strings"
end
path ||= Dir.pwd
- vars = { "PWD" => path }
- options = { chdir: path }
+
+ vars = {
+ "PWD" => path
+ }
+
+ options = {
+ chdir: path
+ }
unless File.directory?(path)
FileUtils.mkdir_p(path)
@@ -19,6 +25,7 @@ module Popen
@cmd_output = ""
@cmd_status = 0
+
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
@cmd_output << stdout.read
@cmd_output << stderr.read
eos
end
before do
@lines = parser.parse(diff.lines)
end
it { @lines.size.should == 30 }
describe 'lines' do
describe 'first line' do
let(:line) { @lines.first }
it { line.type.should == 'match' }
it { line.old_pos.should == 6 }
it { line.new_pos.should == 6 }
it { line.text.should == '@@ -6,12 +6,18 @@ module Popen' }
end
describe 'removal line' do
let(:line) { @lines[10] }
it { line.type.should == 'old' }
it { line.old_pos.should == 14 }
it { line.new_pos.should == 13 }
it { line.text.should == '- options = { chdir: path }' }
end
describe 'addition line' do
let(:line) { @lines[16] }
it { line.type.should == 'new' }
it { line.old_pos.should == 15 }
it { line.new_pos.should == 18 }
it { line.text.should == '+ options = {' }
end
describe 'unchanged line' do
let(:line) { @lines.last }
it { line.type.should == nil }
it { line.old_pos.should == 24 }
it { line.new_pos.should == 31 }
it { line.text.should == ' @cmd_output &lt;&lt; stderr.read' }
end
end
end
end
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -5,16 +5,11 @@ ...@@ -5,16 +5,11 @@
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# token :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # properties :text
# subdomain :string(255)
# room :string(255)
# recipients :text
# api_key :string(255)
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -94,22 +94,50 @@ describe API::API, api: true do ...@@ -94,22 +94,50 @@ describe API::API, api: true do
describe "POST /projects/:id/repository/branches" do describe "POST /projects/:id/repository/branches" do
it "should create a new branch" do it "should create a new branch" do
post api("/projects/#{project.id}/repository/branches", user), post api("/projects/#{project.id}/repository/branches", user),
branch_name: branch_name, branch_name: 'feature1',
ref: branch_sha ref: branch_sha
response.status.should == 201 response.status.should == 201
json_response['name'].should == branch_name json_response['name'].should == 'feature1'
json_response['commit']['id'].should == branch_sha json_response['commit']['id'].should == branch_sha
end end
it "should deny for user without push access" do it "should deny for user without push access" do
post api("/projects/#{project.id}/repository/branches", user2), post api("/projects/#{project.id}/repository/branches", user2),
branch_name: branch_name, branch_name: branch_name,
ref: branch_sha ref: branch_sha
response.status.should == 403 response.status.should == 403
end end
it 'should return 400 if branch name is invalid' do
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new design',
ref: branch_sha
response.status.should == 400
json_response['message'].should == 'Branch name invalid'
end
it 'should return 400 if branch already exists' do
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new_design1',
ref: branch_sha
response.status.should == 201
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new_design1',
ref: branch_sha
response.status.should == 400
json_response['message'].should == 'Branch already exists'
end
it 'should return 400 if ref name is invalid' do
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new_design3',
ref: 'foo'
response.status.should == 400
json_response['message'].should == 'Invalid reference name'
end
end end
describe "DELETE /projects/:id/repository/branches/:branch" do describe "DELETE /projects/:id/repository/branches/:branch" do
...@@ -120,6 +148,11 @@ describe API::API, api: true do ...@@ -120,6 +148,11 @@ describe API::API, api: true do
response.status.should == 200 response.status.should == 200
end end
it 'should return 404 if branch not exists' do
delete api("/projects/#{project.id}/repository/branches/foobar", user)
response.status.should == 404
end
it "should remove protected branch" do it "should remove protected branch" do
project.protected_branches.create(name: branch_name) project.protected_branches.create(name: branch_name)
delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
......
...@@ -9,6 +9,7 @@ describe API::API, api: true do ...@@ -9,6 +9,7 @@ describe API::API, api: true do
let!(:label) do let!(:label) do
create(:label, title: 'label', color: '#FFAABB', project: project) create(:label, title: 'label', color: '#FFAABB', project: project)
end end
let!(:label_link) { create(:label_link, label: label, target: issue) }
before { project.team << [user, :reporter] } before { project.team << [user, :reporter] }
...@@ -58,6 +59,45 @@ describe API::API, api: true do ...@@ -58,6 +59,45 @@ describe API::API, api: true do
json_response.first['id'].should == issue.id json_response.first['id'].should == issue.id
json_response.second['id'].should == closed_issue.id json_response.second['id'].should == closed_issue.id
end end
it 'should return an array of labeled issues' do
get api("/issues?labels=#{label.title}", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['labels'].should == [label.title]
end
it 'should return an array of labeled issues when at least one label matches' do
get api("/issues?labels=#{label.title},foo,bar", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['labels'].should == [label.title]
end
it 'should return an empty array if no issue matches labels' do
get api('/issues?labels=foo,bar', user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 0
end
it 'should return an array of labeled issues matching given state' do
get api("/issues?labels=#{label.title}&state=opened", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['labels'].should == [label.title]
json_response.first['state'].should == 'opened'
end
it 'should return an empty array if no issue matches labels and state filters' do
get api("/issues?labels=#{label.title}&state=closed", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 0
end
end end
end end
...@@ -68,6 +108,29 @@ describe API::API, api: true do ...@@ -68,6 +108,29 @@ describe API::API, api: true do
json_response.should be_an Array json_response.should be_an Array
json_response.first['title'].should == issue.title json_response.first['title'].should == issue.title
end end
it 'should return an array of labeled project issues' do
get api("/projects/#{project.id}/issues?labels=#{label.title}", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['labels'].should == [label.title]
end
it 'should return an array of labeled project issues when at least one label matches' do
get api("/projects/#{project.id}/issues?labels=#{label.title},foo,bar", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['labels'].should == [label.title]
end
it 'should return an empty array if no project issue matches labels' do
get api("/projects/#{project.id}/issues?labels=foo,bar", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 0
end
end end
describe "GET /projects/:id/issues/:issue_id" do describe "GET /projects/:id/issues/:issue_id" do
...@@ -182,7 +245,7 @@ describe API::API, api: true do ...@@ -182,7 +245,7 @@ describe API::API, api: true do
labels: 'label2', state_event: "close" labels: 'label2', state_event: "close"
response.status.should == 200 response.status.should == 200
json_response['labels'].should == ['label2'] json_response['labels'].should include 'label2'
json_response['state'].should eq "closed" json_response['state'].should eq "closed"
end end
end end
......
...@@ -114,6 +114,7 @@ describe API::API, api: true do ...@@ -114,6 +114,7 @@ describe API::API, api: true do
it "should assign attributes to project" do it "should assign attributes to project" do
project = attributes_for(:project, { project = attributes_for(:project, {
path: 'camelCasePath',
description: Faker::Lorem.sentence, description: Faker::Lorem.sentence,
issues_enabled: false, issues_enabled: false,
merge_requests_enabled: false, merge_requests_enabled: false,
...@@ -123,7 +124,6 @@ describe API::API, api: true do ...@@ -123,7 +124,6 @@ describe API::API, api: true do
post api("/projects", user), project post api("/projects", user), project
project.each_pair do |k,v| project.each_pair do |k,v|
next if k == :path
json_response[k.to_s].should == v json_response[k.to_s].should == v
end end
end end
......
...@@ -23,22 +23,67 @@ describe API::API, api: true do ...@@ -23,22 +23,67 @@ describe API::API, api: true do
end end
describe 'POST /projects/:id/repository/tags' do describe 'POST /projects/:id/repository/tags' do
it 'should create a new tag' do context 'lightweight tags' do
post api("/projects/#{project.id}/repository/tags", user), it 'should create a new tag' do
tag_name: 'v1.0.0', post api("/projects/#{project.id}/repository/tags", user),
ref: 'master' tag_name: 'v7.0.1',
ref: 'master'
response.status.should == 201
json_response['name'].should == 'v1.0.0' response.status.should == 201
json_response['name'].should == 'v7.0.1'
end
end end
# TODO: fix this test for CI
#context 'annotated tag' do
#it 'should create a new annotated tag' do
#post api("/projects/#{project.id}/repository/tags", user),
#tag_name: 'v7.1.0',
#ref: 'master',
#message: 'tag message'
#response.status.should == 201
#json_response['name'].should == 'v7.1.0'
# The message is not part of the JSON response.
# Additional changes to the gitlab_git gem may be required.
# json_response['message'].should == 'tag message'
#end
#end
it 'should deny for user without push access' do it 'should deny for user without push access' do
post api("/projects/#{project.id}/repository/tags", user2), post api("/projects/#{project.id}/repository/tags", user2),
tag_name: 'v1.0.0', tag_name: 'v1.9.0',
ref: '621491c677087aa243f165eab467bfdfbee00be1' ref: '621491c677087aa243f165eab467bfdfbee00be1'
response.status.should == 403 response.status.should == 403
end end
it 'should return 400 if tag name is invalid' do
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'v 1.0.0',
ref: 'master'
response.status.should == 400
json_response['message'].should == 'Tag name invalid'
end
it 'should return 400 if tag already exists' do
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'v8.0.0',
ref: 'master'
response.status.should == 201
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'v8.0.0',
ref: 'master'
response.status.should == 400
json_response['message'].should == 'Tag already exists'
end
it 'should return 400 if ref name is invalid' do
post api("/projects/#{project.id}/repository/tags", user),
tag_name: 'mytag',
ref: 'foo'
response.status.should == 400
json_response['message'].should == 'Invalid reference name'
end
end end
describe "GET /projects/:id/repository/tree" do describe "GET /projects/:id/repository/tree" do
......
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