Commit cfc4a2db authored by Franz-Robert van Vugt's avatar Franz-Robert van Vugt

Merge branch 'master' of https://github.com/gitlabhq/gitlabhq into patch-1

parents f03820eb fc5ac145
--color --drb
--color
......@@ -19,10 +19,10 @@ rvm:
services:
- redis-server
before_script:
- "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
- "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
v 6.8.0
- Ability to at mention users that are participating in issue and merge req. discussion
- Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu)
- Make user search case-insensitive (Christopher Arnold)
v 6.7.2
- Fix upgrader script
v 6.7.1
- Fix GitLab CI integration
v 6.7.0
- Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations
- Add support for Gemnasium as a Project Service (Olivier Gonzalez)
......@@ -9,6 +20,7 @@ v 6.7.0
- Show contribution guide link for new issue form (Jeroen van Baarsen)
- Fix CI status for merge requests from fork
- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
- New page load indicator that includes a spinner that scrolls with the page
- Converted all the help sections into markdown
- LDAP user filters
- Streamline the content of notification emails (Pierre de La Morinerie)
......@@ -24,6 +36,12 @@ v 6.7.0
- Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
- Create and Update MR calls now support the description parameter (Greg Messner)
- Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository
- Added Slack service integration (Federico Ravasio)
- Better API responses for access_levels (sponsored by O'Reilly Media)
- Requires at least 2 unicorn workers
- Requires gitlab-shell v1.9+
- Replaced gemoji(due to closed licencing problem) with Phantom Open Emoji library(combined SIL Open Font License, MIT License and the CC 3.0 License)
- Fix `/:username.keys` response content type (Dmitry Medvinsky)
v 6.6.5
- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
......
......@@ -24,9 +24,12 @@ Issues and merge requests should be in English and contain appropriate language
To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/).
The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue.
The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious errors in the latest [stable or development release of GitLab](MAINTENANCE.md).
If something is wrong but it is not a regression compared to older versions of GitLab please do not open an issue but a feature request.
When submitting an issue please conform to the issue submission guidelines listed below.
Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue.
Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose.
Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose.
Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
......@@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai
## Merge requests
We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI.
We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feature request forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI.
### Merge request guidelines
......@@ -64,7 +67,8 @@ If you can, please submit a merge request with the fix or improvements including
1. The MR title should describes the change you want to make
1. The MR description should give a motive for your change and the method you used to achieve it
1. If the MR changes the UI it should include before and after screenshots
1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feedback items](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR
1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R`
1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR
1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
......@@ -74,6 +78,14 @@ Please keep the change in a single MR **as small as possible**. If you want to c
For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). Please ensure that your merge request meets the following contribution acceptance criteria.
**Please format your merge request description as follows:**
1. What does this MR do?
2. Are there points in the code the reviewer needs to double check?
3. Why was this MR needed?
4. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
5. Screenshots (If appropiate)
## Contribution acceptance criteria
1. The change is as small as possible (see the above paragraph for details)
......
......@@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2"
# Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2"
# Slack integration
gem "slack-notifier", "~> 0.2.0"
# d3
gem "d3_rails", "~> 3.1.4"
......@@ -162,8 +165,9 @@ gem "modernizr", "2.6.2"
gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 3.2'
gem "gemoji", "~> 1.3.0"
gem "gitlab_emoji", "~> 0.0.1.1"
gem "gon", '~> 5.0.0'
gem 'nprogress-rails'
group :development do
gem "annotate", "~> 2.6.0.beta2"
......@@ -214,7 +218,6 @@ group :development, :test do
# PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.4.1'
gem 'spork', '~> 1.0rc'
gem 'jasmine', '2.0.0.rc5'
gem "spring", '1.1.1'
......
......@@ -128,6 +128,8 @@ GEM
mail (~> 2.2)
email_validator (1.4.0)
activemodel
emoji (1.0.1)
json
enumerize (0.7.0)
activesupport (>= 3.2)
equalizer (0.0.8)
......@@ -165,7 +167,6 @@ GEM
formatador (0.2.4)
gemnasium-gitlab-service (0.2.1)
rugged (~> 0.19)
gemoji (1.3.1)
gherkin-ruby (0.3.1)
racc
github-markdown (0.5.5)
......@@ -190,6 +191,8 @@ GEM
charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.4)
mime-types (~> 1.19)
gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1)
gitlab_git (5.7.1)
activesupport (~> 4.0.0)
charlock_holmes (~> 0.6.9)
......@@ -297,6 +300,7 @@ GEM
net-ssh (>= 1.99.1)
net-ssh (2.7.0)
nokogiri (1.5.10)
nprogress-rails (0.1.2.3)
oauth (0.4.7)
oauth2 (0.8.1)
faraday (~> 0.8)
......@@ -468,6 +472,7 @@ GEM
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
six (0.2.0)
slack-notifier (0.2.0)
slim (2.0.2)
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
......@@ -479,7 +484,6 @@ GEM
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
spork (1.0.0rc4)
spring (1.1.1)
spring-commands-rspec (1.0.1)
spring (>= 0.9.1)
......@@ -591,12 +595,12 @@ DEPENDENCIES
font-awesome-rails (~> 3.2)
foreman
gemnasium-gitlab-service (~> 0.2)
gemoji (~> 1.3.0)
github-markup (~> 0.7.4)!
gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-gollum-lib (~> 1.1.0)
gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1)
gitlab_git (~> 5.7.1)
gitlab_meta (= 6.0)
gitlab_omniauth-ldap (= 1.0.4)
......@@ -620,6 +624,7 @@ DEPENDENCIES
minitest (~> 4.7.0)
modernizr (= 2.6.2)
mysql2
nprogress-rails
omniauth (~> 1.1.3)
omniauth-github
omniauth-google-oauth2
......@@ -652,9 +657,9 @@ DEPENDENCIES
simplecov
sinatra
six
slack-notifier (~> 0.2.0)
slim
spinach-rails
spork (~> 1.0rc)
spring (= 1.1.1)
spring-commands-rspec (= 1.0.1)
spring-commands-spinach (= 1.0.0)
......
......@@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co
- Monitors for new merge requests (at least once a week)
- Manages their work queue by looking at issues and merge requests assigned to them
- Close fixed issues (via commit messages or manually)
- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)!
- Response guidelines
- Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to merge requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review).
## Priorities of the issue team
......@@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue
### Feature requests
Thanks for your interest in GitLab. We don't use the issue tracker for feature requests. Please use http://feedback.gitlab.com/ for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information.
Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please the [feature request forum](http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information.
### Issue report for old version
......
web: bundle exec unicorn_rails -p $PORT -E development
web: bundle exec unicorn_rails -p $PORT -E development -c config/unicorn_development.rb
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
......@@ -71,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl
It is recommended to follow a monthly upgrade schedule. Security releases come out when needed. For more information about the release process see the documentation for [monthly](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/monthly.md) and [security](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/security.md) releases.
* Features that will be in the next releases are listed on [the feedback and suggestions 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).
* Features that will be in the next releases are listed 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).
### Run in production mode
......@@ -96,14 +96,9 @@ or start each component separately
### Run the tests
* Seed the database
bundle exec rake db:setup RAILS_ENV=test
bundle exec rake db:seed_fu RAILS_ENV=test
* Run all tests
bundle exec rake gitlab:test RAILS_ENV=test
bundle exec rake test
* [RSpec](http://rspec.info/) unit and functional tests
......@@ -134,22 +129,4 @@ or start each component separately
### Getting help
* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported.
* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems.
* [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix.
* [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.
* [Contributing guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) describes how to submit merge requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed.
* [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations.
* [#gitlab IRC channel](http://www.freenode.net/) on Freenode to get in touch with other GitLab users and get help, it's managed by James Newton (newton), Drew Blessing (dblessing), and Sam Gleske (sag47).
* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.
* [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help.
Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
6.7.0.pre
6.8.0.pre
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery.ui.all
//= require jquery_ujs
//= require jquery.cookie
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.history
//= require jquery.waitforimages
//= require jquery.atwho
//= require jquery.scrollto
//= require jquery.blockUI
//= require turbolinks
//= require jquery.turbolinks
//= require bootstrap
//= require modernizr
//= require select2
//= require raphael
//= require g.raphael-min
//= require g.bar-min
//= require branch-graph
//= require highlightjs.min
//= require ace/ace
//= require_tree .
//= require d3
//= require underscore
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require jquery
#= require jquery.ui.all
#= require jquery_ujs
#= require jquery.cookie
#= require jquery.endless-scroll
#= require jquery.highlight
#= require jquery.history
#= require jquery.waitforimages
#= require jquery.atwho
#= require jquery.scrollto
#= require jquery.blockUI
#= require turbolinks
#= require jquery.turbolinks
#= require bootstrap
#= require modernizr
#= require select2
#= require raphael
#= require g.raphael-min
#= require g.bar-min
#= require branch-graph
#= require highlightjs.min
#= require ace/ace
#= require d3
#= require underscore
#= require nprogress
#= require nprogress-turbolinks
#= require_tree .
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......@@ -41,19 +75,11 @@ window.linkify = (str) ->
window.simpleFormat = (str) ->
linkify(sanitize(str).replace(/\n/g, '<br />'))
window.startSpinner = ->
$('.turbolink-spinner').fadeIn()
window.stopSpinner = ->
$('.turbolink-spinner').fadeOut()
window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
document.addEventListener("page:fetch", startSpinner)
document.addEventListener("page:fetch", unbindEvents)
document.addEventListener("page:change", stopSpinner)
$ ->
# Click a .one_click_select field, select the contents
......
......@@ -6,7 +6,6 @@ GitLab.GfmAutoComplete =
dataSource: ''
# Emoji
Emoji:
assetBase: ''
template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
# Team Members
......@@ -27,7 +26,7 @@ GitLab.GfmAutoComplete =
tpl: @Emoji.template
callbacks:
before_save: (emojis) =>
$.map emojis, (em) => name: em, insert: em+ ':', image: "#{@Emoji.assetBase}/#{em}.png"
$.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path
# Team Members
input.atwho
......
......@@ -7,6 +7,8 @@
*= require select2
*= require highlightjs.min
*= require_self
*= require nprogress
*= require nprogress-bootstrap
*/
@import "main/variables.scss";
......
......@@ -355,3 +355,7 @@ table {
@media (max-width: $screen-xs-max) {
.container .content { margin-top: 20px; }
}
.wiki .highlight, .note-body .highlight {
margin-bottom: 9px;
}
......@@ -88,9 +88,6 @@ a:focus {
.wiki {
@include md-typography;
font-size: 14px;
line-height: 1.6;
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
......@@ -120,3 +117,11 @@ a:focus {
.md {
@include md-typography;
}
/**
* Textareas intended for GFM
*
*/
textarea.js-gfm-input {
font-family: $monospace_font;
}
......@@ -84,6 +84,9 @@
}
@mixin md-typography {
font-size: 14px;
line-height: 1.6;
img {
max-width: 100%;
}
......
/* Generic print styles */
header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;}
.profiler-results {display: none;}
/* Styles targeted specifically at printing files */
.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;}
.file-title {display: none;}
.file-holder {border: none;}
.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
.wiki h1 {font-size: 30px;}
.wiki h2 {font-size: 22px;}
.wiki h3 {font-size: 18px; font-weight: bold; }
......@@ -61,7 +61,7 @@
}
.project-row, .group-row {
padding: 10px 12px !important;
padding: 8px 12px !important;
font-size: 14px;
line-height: 24px;
......
......@@ -62,6 +62,29 @@
font-size: 12px;
}
}
.text-file-parallel div {
display: inline-block;
padding-bottom: 16px;
}
.diff-side {
overflow-x: scroll;
width: 508px;
height: 700px;
}
.diff-side.diff-side-left{
overflow-y:hidden;
}
.diff-side table, td.diff-middle table {
height: 700px;
}
.diff-middle {
width: 114px;
vertical-align: top;
height: 700px;
overflow: hidden
}
.old_line, .new_line, .diff_line {
margin: 0px;
padding: 0px;
......@@ -125,8 +148,6 @@
}
&.parallel {
display: table-cell;
overflow: hidden;
width: 50%;
}
}
}
......
......@@ -42,7 +42,7 @@
}
}
padding: 14px 0px;
padding: 12px 0px;
border-bottom: 1px solid #eee;
.event-title {
color: #333;
......@@ -63,6 +63,10 @@
color: #666;
margin-top: 5px;
.md {
font-size: 13px;
}
pre {
border: none;
background: #f9f9f9;
......
......@@ -273,3 +273,9 @@ header {
}
}
}
@media (max-width: $screen-xs-max) {
#nprogress .spinner {
right: 35px !important;
}
}
......@@ -27,11 +27,15 @@ ul.notes {
.discussion-last-update,
.note-last-update {
font-style: italic;
&:before {
content: "\00b7";
}
font-size: 13px;
}
.author {
color: $style_color;
color: #555;
font-weight: bold;
font-size: 14px;
&:hover {
color: $primary_color;
}
......
class PasswordsController < Devise::PasswordsController
def create
email = resource_params[:email]
resource_found = resource_class.find_by_email(email)
if resource_found && resource_found.ldap_user?
flash[:alert] = "Cannot reset password for LDAP user."
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return
end
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
end
......@@ -41,7 +41,7 @@ class Profiles::KeysController < ApplicationController
begin
user = User.find_by_username(params[:username])
if user.present?
render text: user.all_ssh_keys.join("\n")
render text: user.all_ssh_keys.join("\n"), content_type: "text/plain"
else
render_404 and return
end
......
......@@ -76,7 +76,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update
@issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id))
@issue.update_attributes(params[:issue])
@issue.reset_events_cache
respond_to do |format|
......
......@@ -109,7 +109,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_request].delete(:source_project_id)
params[:merge_request].delete(:target_project_id)
if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id))
if @merge_request.update_attributes(params[:merge_request])
@merge_request.reset_events_cache
respond_to do |format|
......
......@@ -38,7 +38,6 @@ class Projects::MilestonesController < Projects::ApplicationController
def create
@milestone = @project.milestones.new(params[:milestone])
@milestone.author_id_of_changes = current_user.id
if @milestone.save
redirect_to project_milestone_path(@project, @milestone)
......@@ -48,7 +47,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def update
@milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id))
@milestone.update_attributes(params[:milestone])
respond_to do |format|
format.js
......
......@@ -123,11 +123,20 @@ class ProjectsController < ApplicationController
end
def autocomplete_sources
note_type = params['type']
note_id = params['type_id']
participating = if note_type && note_id
participants_in(note_type, note_id)
else
[]
end
team_members = sorted(@project.team.members)
participants = team_members + participating
@suggestions = {
emojis: Emoji.names,
emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
issues: @project.issues.select([:iid, :title, :description]),
mergerequests: @project.merge_requests.select([:iid, :title, :description]),
members: @project.team.members.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
members: participants.uniq
}
respond_to do |format|
......@@ -162,4 +171,25 @@ class ProjectsController < ApplicationController
def user_layout
current_user ? "projects" : "public_projects"
end
def participants_in(type, id)
users = case type
when "Issue"
issue = @project.issues.find_by_iid(id)
issue ? issue.participants : []
when "MergeRequest"
merge_request = @project.merge_requests.find_by_iid(id)
merge_request ? merge_request.participants : []
when "Commit"
author_ids = Note.for_commit_id(id).pluck(:author_id).uniq
User.where(id: author_ids)
else
[]
end
sorted(users)
end
def sorted(users)
users.uniq.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
end
end
......@@ -47,9 +47,9 @@ class BaseFinder
[]
end
elsif current_user && params[:authorized_only].presence
klass.of_projects(current_user.authorized_projects)
klass.of_projects(current_user.authorized_projects).references(:project)
else
klass.of_projects(Project.accessible_to(current_user))
klass.of_projects(Project.accessible_to(current_user)).references(:project)
end
end
......
......@@ -105,8 +105,80 @@ module CommitsHelper
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end
def get_old_file(project, commit, diff)
project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
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.sloc + added_lines.length : file.sloc
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
protected
......
......@@ -20,7 +20,7 @@ module MergeRequestsHelper
target_project_id: target_project.id,
source_branch: event.branch_name,
target_branch: target_project.repository.root_ref,
title: event.branch_name.titleize
title: event.branch_name.humanize
}
end
......
......@@ -40,7 +40,7 @@ module TreeHelper
# Returns boolean
def markup?(filename)
filename.downcase.end_with?(*%w(.textile .rdoc .org .creole
.mediawiki .rst .asciidoc .pod))
.mediawiki .rst .adoc .asciidoc .pod))
end
def gitlab_markdown?(filename)
......
......@@ -29,11 +29,11 @@ module Emails
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
end
def merged_merge_request_email(recipient_id, merge_request_id)
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(@merge_request.author_id_of_changes),
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
end
......
......@@ -37,8 +37,6 @@ module Issuable
allow_nil: true,
prefix: true
attr_accessor :author_id_of_changes
attr_mentionable :title, :description
end
......
class DiffLine
attr_accessor :type, :content, :num, :code
end
......@@ -47,14 +47,6 @@ class Event < ActiveRecord::Base
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
class << self
def determine_action(record)
if [Issue, MergeRequest].include? record.class
Event::CREATED
elsif record.kind_of? Note
Event::COMMENTED
end
end
def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads')
commit = project.repository.commit(ref.target)
......
......@@ -30,8 +30,7 @@ class Issue < ActiveRecord::Base
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
attr_accessible :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :author_id_of_changes,
:state_event
:milestone_id, :label_list, :state_event
acts_as_taggable_on :labels
......
......@@ -38,7 +38,7 @@ class MergeRequest < ActiveRecord::Base
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event, :description
attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description
attr_accessor :should_remove_source_branch
......
......@@ -16,8 +16,7 @@
class Milestone < ActiveRecord::Base
include InternalId
attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes
attr_accessor :author_id_of_changes
attr_accessible :title, :description, :due_date, :state_event
belongs_to :project
has_many :issues
......@@ -89,6 +88,6 @@ class Milestone < ActiveRecord::Base
end
def author_id
author_id_of_changes
nil
end
end
......@@ -199,7 +199,8 @@ class Note < ActiveRecord::Base
def downvote?
votable? && (note.start_with?('-1') ||
note.start_with?(':-1:') ||
note.start_with?(':thumbsdown:')
note.start_with?(':thumbsdown:') ||
note.start_with?(':thumbs_down_sign:')
)
end
......@@ -249,7 +250,8 @@ class Note < ActiveRecord::Base
def upvote?
votable? && (note.start_with?('+1') ||
note.start_with?(':+1:') ||
note.start_with?(':thumbsup:')
note.start_with?(':thumbsup:') ||
note.start_with?(':thumbs_up_sign:')
)
end
......
......@@ -56,6 +56,7 @@ class Project < ActiveRecord::Base
has_one :flowdock_service, dependent: :destroy
has_one :assembla_service, dependent: :destroy
has_one :gemnasium_service, dependent: :destroy
has_one :slack_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it
......@@ -304,7 +305,7 @@ class Project < ActiveRecord::Base
end
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium)
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack)
end
def gitlab_ci?
......
require 'slack-notifier'
class SlackMessage
def initialize(params)
@after = params.fetch(:after)
@before = params.fetch(:before)
@commits = params.fetch(:commits, [])
@project_name = params.fetch(:project_name)
@project_url = params.fetch(:project_url)
@ref = params.fetch(:ref).gsub('refs/heads/', '')
@username = params.fetch(:user_name)
end
def compose
format(message)
end
private
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :username
def message
if new_branch?
new_branch_message
elsif removed_branch?
removed_branch_message
else
push_message << commit_messages
end
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def new_branch_message
"#{username} pushed new branch #{branch_link} to #{project_link}"
end
def removed_branch_message
"#{username} removed branch #{ref} from #{project_link}"
end
def push_message
"#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
end
def commit_messages
commits.each_with_object('') do |commit, str|
str << compose_commit_message(commit)
end
end
def compose_commit_message(commit)
id = commit.fetch(:id)[0..5]
message = commit.fetch(:message)
url = commit.fetch(:url)
"\n - #{message} ([#{id}](#{url}))"
end
def new_branch?
before =~ /000000/
end
def removed_branch?
after =~ /000000/
end
def branch_url
"#{project_url}/commits/#{ref}"
end
def compare_url
"#{project_url}/compare/#{before}...#{after}"
end
def branch_link
"[#{ref}](#{branch_url})"
end
def project_link
"[#{project_name}](#{project_url})"
end
def compare_link
"[Compare changes](#{compare_url})"
end
end
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# token :string(255)
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
# api_key :string(255)
#
class SlackService < Service
attr_accessible :room
attr_accessible :subdomain
validates :room, presence: true, if: :activated?
validates :subdomain, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
def title
'Slack'
end
def description
'A team communication tool for the 21st century'
end
def to_param
'slack'
end
def fields
[
{ type: 'text', name: 'subdomain', placeholder: '' },
{ type: 'text', name: 'token', placeholder: '' },
{ type: 'text', name: 'room', placeholder: 'Ex. #general' },
]
end
def execute(push_data)
message = SlackMessage.new(push_data.merge(
project_url: project_url,
project_name: project_name
))
notifier = Slack::Notifier.new(subdomain, token)
notifier.channel = room
notifier.username = 'GitLab'
notifier.ping(message.compose)
end
private
def project_name
project.name_with_namespace.gsub(/\s/, '')
end
def project_url
project.web_url
end
end
......@@ -204,7 +204,7 @@ class User < ActiveRecord::Base
end
def search query
where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%")
where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
end
def by_username_or_id(name_or_id)
......@@ -249,7 +249,7 @@ class User < ActiveRecord::Base
def namespace_uniq
namespace_name = self.username
if Namespace.find_by(path: namespace_name)
self.errors.add :username, "already exist"
self.errors.add :username, "already exists"
end
end
......
class ActivityObserver < BaseObserver
observe :issue, :note, :milestone
def after_create(record)
event_author_id = record.author_id
if record.kind_of?(Note)
# Skip system notes, like status changes and cross-references.
return true if record.system?
# Skip wall notes to prevent spamming of dashboard
return true if record.noteable_type.blank?
end
if event_author_id
create_event(record, Event.determine_action(record))
end
end
def after_close(record, transition)
create_event(record, Event::CLOSED)
end
def after_reopen(record, transition)
create_event(record, Event::REOPENED)
end
protected
def create_event(record, status)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
end
......@@ -3,6 +3,10 @@ class BaseObserver < ActiveRecord::Observer
NotificationService.new
end
def event_service
EventCreateService.new
end
def log_info message
Gitlab::AppLogger.info message
end
......
class IssueObserver < BaseObserver
def after_create(issue)
notification.new_issue(issue, current_user)
event_service.open_issue(issue, current_user)
issue.create_cross_references!(issue.project, current_user)
execute_hooks(issue)
end
def after_close(issue, transition)
notification.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue)
execute_hooks(issue)
end
def after_reopen(issue, transition)
event_service.reopen_issue(issue, current_user)
create_note(issue)
execute_hooks(issue)
end
......
class MergeRequestObserver < ActivityObserver
observe :merge_request
class MergeRequestObserver < BaseObserver
def after_create(merge_request)
if merge_request.author_id
create_event(merge_request, Event.determine_action(merge_request))
end
event_service.open_mr(merge_request, current_user)
notification.new_merge_request(merge_request, current_user)
merge_request.create_cross_references!(merge_request.project, current_user)
execute_hooks(merge_request)
end
def after_close(merge_request, transition)
create_event(merge_request, Event::CLOSED)
event_service.close_mr(merge_request, current_user)
notification.close_mr(merge_request, current_user)
create_note(merge_request)
execute_hooks(merge_request)
end
def after_reopen(merge_request, transition)
create_event(merge_request, Event::REOPENED)
event_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
execute_hooks(merge_request)
merge_request.reload_code
......@@ -33,16 +28,6 @@ class MergeRequestObserver < ActivityObserver
execute_hooks(merge_request)
end
def create_event(record, status)
Event.create(
project: record.target_project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
private
# Create merge request note with service comment like 'Status changed to closed'
......
class MilestoneObserver < BaseObserver
def after_create(milestone)
event_service.open_milestone(milestone, current_user)
end
def after_close(milestone, transition)
event_service.close_milestone(milestone, current_user)
end
def after_reopen(milestone, transition)
event_service.reopen_milestone(milestone, current_user)
end
end
......@@ -2,6 +2,12 @@ class NoteObserver < BaseObserver
def after_create(note)
notification.new_note(note)
# Skip system notes, like status changes and cross-references.
# Skip wall notes to prevent spamming of dashboard
if note.noteable_type.present? && !note.system
event_service.leave_note(note, current_user)
end
unless note.system?
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
......
# EventCreateService class
#
# Used for creating events feed on dashboard after certain user action
#
# Ex.
# EventCreateService.new.new_issue(issue, current_user)
#
class EventCreateService
def open_issue(issue, current_user)
create_event(issue, current_user, Event::CREATED)
end
def close_issue(issue, current_user)
create_event(issue, current_user, Event::CLOSED)
end
def reopen_issue(issue, current_user)
create_event(issue, current_user, Event::REOPENED)
end
def open_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CREATED)
end
def close_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CLOSED)
end
def reopen_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::REOPENED)
end
def merge_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::MERGED)
end
def open_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CREATED)
end
def close_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CLOSED)
end
def reopen_milestone(milestone, current_user)
create_event(milestone, current_user, Event::REOPENED)
end
def leave_note(note, current_user)
create_event(note, current_user, Event::COMMENTED)
end
private
def create_event(record, current_user, status)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
end
......@@ -9,11 +9,10 @@ module MergeRequests
merge_request.lock
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.author_id_of_changes = current_user.id
merge_request.merge
notification.merge_mr(merge_request)
create_merge_event(merge_request)
notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request)
true
......
......@@ -7,14 +7,8 @@ module MergeRequests
NotificationService.new
end
def create_merge_event(merge_request)
Event.create(
project: merge_request.target_project,
target_id: merge_request.id,
target_type: merge_request.class.name,
action: Event::MERGED,
author_id: merge_request.author_id_of_changes
)
def create_merge_event(merge_request, current_user)
EventCreateService.new.merge_mr(merge_request, current_user)
end
def execute_project_hooks(merge_request)
......
......@@ -7,11 +7,10 @@ module MergeRequests
# to target branch
class MergeService < BaseMergeService
def execute(merge_request, current_user, commit_message)
merge_request.author_id_of_changes = current_user.id
merge_request.merge
notification.merge_mr(merge_request)
create_merge_event(merge_request)
notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request)
true
......
......@@ -86,12 +86,12 @@ class NotificationService
# * merge_request assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating
#
def merge_mr(merge_request)
def merge_mr(merge_request, current_user)
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project)
recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq
recipients.each do |recipient|
mailer.merged_merge_request_email(recipient.id, merge_request.id)
mailer.merged_merge_request_email(recipient.id, merge_request.id, current_user.id)
end
end
......@@ -111,6 +111,7 @@ class NotificationService
# ignore gitlab service messages
return true if note.note =~ /\A_Status changed to closed_/
return true if note.note =~ /\A_mentioned in / && note.system == true
opts = { noteable_type: note.noteable_type, project_id: note.project_id }
......
......@@ -2,10 +2,12 @@
- if providers.present?
%hr
%div{:'data-no-turbolink' => 'data-no-turbolink'}
%span Sign in with: &nbsp;
%span Sign in with*: &nbsp;
- providers.each do |provider|
%span
- if default_providers.include?(provider)
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
- else
= link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn"
%br
%small * Make sure your email address is public
......@@ -4,7 +4,8 @@
= "#{title} | " if defined?(title)
GitLab
= favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application"
= stylesheet_link_tag "application", :media => "all"
= stylesheet_link_tag "print", :media => "print"
= javascript_include_tag "application"
= csrf_meta_tags
= include_gon
......
......@@ -14,10 +14,6 @@
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li.hidden-sm.hidden-xs
%a
%div.hide.turbolink-spinner
%i.icon-refresh.icon-spin
%li.hidden-sm.hidden-xs
= render "layouts/search"
%li.visible-sm.visible-xs
......
:javascript
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project)}"
GitLab.GfmAutoComplete.Emoji.assetBase = "#{Gitlab.config.gitlab.relative_url_root + '/assets/emoji'}"
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project, type: @noteable.class, type_id: params[:id])}"
GitLab.GfmAutoComplete.setup();
......@@ -8,11 +8,15 @@
%span.separator
%h1.title= title
.pull-right
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
%span.sr-only Toggle navigation
%i.icon-reorder
.pull-right.hidden-xs
= link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new'
%ul.nav.navbar-nav
%li
%a
%div.hide.turbolink-spinner
%i.icon-refresh.icon-spin
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li.visible-xs
= link_to "Sign in", new_session_path(:user)
......@@ -4,7 +4,7 @@
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/public_head_panel", title: project_title(@project)
%nav.main-nav
%nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/project'
.container
.content= yield
......@@ -7,7 +7,7 @@
%li
#{commit.short_id} - #{commit.title}
%h4 Diff:
%h4 Changes:
- @diffs.each do |diff|
%li
%strong
......@@ -23,6 +23,6 @@
%br
- if @compare.timeout
%h5 Huge diff. To prevent performance issues it was hidden
%h5 To prevent performance issues changes are hidden
- elsif @compare.commits_over_limit?
%h5 Diff for big amount of commits is disabled
%h5 Changes are not shown due to large amount of commits
......@@ -6,7 +6,7 @@ Commits:
#{commit.short_id} - #{truncate(commit.title, length: 40)}
\
\
Diff:
Changes:
- @diffs.each do |diff|
\
\=====================================
......@@ -22,4 +22,4 @@ Diff:
- if @compare.timeout
Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit?
Diff for big amount of commits is disabled
Changes are not shown due to large amount of commits
/ Side-by-side diff view
- old_file = get_old_file(project, @commit, diff)
- deleted_lines = {}
- added_lines = {}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
- 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 }
- max_length = old_file.sloc + added_lines.length if old_file
- max_length ||= file.sloc
- offset1 = 0
- offset2 = 0
- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- num_lines = old_lines.length
%div.text-file-parallel
%table{ style: "table-layout: fixed;" }
- 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]
%div.diff-side.diff-side-left
%table
- old_lines.each do |line|
%tr.line_holder.parallel
- if line.type == :file_created
%td.line_content.parallel= "File was created"
- elsif line.type == :deleted
%td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
%div.diff-middle
%table
- num_lines.times do |index|
%tr
- if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else
%td.old_line= old_lines[index].num
%td.diff_line=""
- if deleted_line && added_line
- elsif deleted_line
- new_line = nil
- offset2 += 1
- elsif added_line
- old_line = nil
- offset1 += 1
- if new_lines[index].type == :added
%td.new_line.new= new_lines[index].num
- else
%td.new_line= new_lines[index].num
%tr.line_holder.parallel
- if line_index == 0 && diff.new_file
%td.line_content.parallel= "File was created"
%td.old_line= ""
- elsif deleted_line
%td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line
%td.old_line.old
= line_index1 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code]
- elsif old_line
%td.line_content.parallel= old_line
%td.old_line= line_index1 + 1
- else
%td.line_content.parallel= ""
%td.old_line= ""
%div.diff-side.diff-side-right
%table
- new_lines.each do |line|
%td.diff_line= ""
%tr.line_holder.parallel
- if line.type == :file_deleted
%td.line_content.parallel= "File was deleted"
- elsif line.type == :added
%td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
- if diff.deleted_file && line_index == 0
%td.new_line= ""
%td.line_content.parallel= "File was deleted"
- elsif added_line
%td.new_line.new
= line_index2 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: added_line[:line_code]
%td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line
- elsif new_line
%td.new_line= line_index2 + 1
%td.line_content.parallel= new_line
- else
%td.new_line= ""
%td.line_content.parallel= ""
:javascript
$('.diff-side-right').on('scroll', function(){
$('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop());
$('.diff-side-left').scrollLeft($(this).scrollLeft());
});
- if @reply_allowed
- comments1 = []
- comments2 = []
- comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line
- comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line
- unless comments1.empty? && comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line
\ No newline at end of file
$('.diff-side-left').on('scroll', function(){
$('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
$('.diff-side-right').scrollLeft($(this).scrollLeft());
});
- too_big = diff.diff.lines.count > 1000
- if too_big
%a.supp_diff_link Diff suppressed. Click to show
%a.supp_diff_link Changes suppressed. Click to show
%table.text-file{class: "#{'hide' if too_big}"}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
......
......@@ -18,17 +18,17 @@
- else
%ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Diff
%h4 Changes
- if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line diff is not shown.
%p To preserve performance the line changes are not shown.
- elsif @timeout
.bs-callout.bs-callout-danger
%h4 Diff for this comparison is extremely large.
%p Use command line to browse diff for this comparison.
%h4 Number of changed files for this comparison is extremely large.
%p Use command line to browse through changes for this comparison.
- else
......
......@@ -2,7 +2,7 @@
Deploy key:
= @key.title
%small
created at
created on
= @key.created_at.stamp("Aug 21, 2011")
.back-link
= link_to project_deploy_keys_path(@project) do
......
%div.issue-form-holder
%h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
%hr
- if @repository.contribution_guide && !@issue.persisted?
- if !@repository.empty? && @repository.contribution_guide && !@issue.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
......
- if @repository.contribution_guide && !@merge_request.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
= form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f|
-if @merge_request.errors.any?
.alert.alert-danger
%ul
- @merge_request.errors.full_messages.each do |msg|
%li= msg
.row
.col-sm-2
.col-sm-10
- if @repository.contribution_guide && !@merge_request.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info
Please review the
%strong #{link_to "guidelines for contribution", contribution_guide_url}
to this repository.
-if @merge_request.errors.any?
.alert.alert-danger
- @merge_request.errors.full_messages.each do |msg|
%div= msg
.merge-request-branches
.form-group
......@@ -47,24 +52,6 @@
= f.text_area :description, class: "form-control js-gfm-input", rows: 14
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
%hr
.form-group
.merge-request-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 btn-small assign-to-me-link'
.form-group
.merge-request-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-actions
- if @merge_request.new_record?
= f.submit 'Submit merge request', class: "btn btn-create"
......@@ -96,7 +83,3 @@
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
});
$('.assign-to-me-link').on('click', function(e){
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
......@@ -20,7 +20,7 @@
%li.diffs-tab{data: {action: 'diffs'}}
= link_to diffs_project_merge_request_path(@project, @merge_request) do
%i.icon-list-alt
Diff
Changes
- content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request)
......
......@@ -3,5 +3,5 @@
var mrTitle = $('#merge_request_title');
if(mrTitle.val().length == 0) {
mrTitle.val("#{params[:ref].titleize}");
mrTitle.val("#{params[:ref].humanize}");
}
......@@ -5,7 +5,7 @@
- else
.bs-callout.bs-callout-warning
%h4
Diff for this comparison is extremely large.
Changes view for this comparison is extremely large.
%p
You can
= link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink"
......
- unless @allowed_to_merge
.bs-callout
%strong You don't have permission to merge this MR
- if @project.archived?
.bs-callout.bs-callout-warning
%strong Archived projects cannot be committed to!
- else
.bs-callout
.automerge_widget.cannot_be_merged.hide
%strong This can't be merged automatically, even if it could be merged you don't have the permission to do so.
.automerge_widget.can_be_merged.hide
%strong This can be merged automatically but you don't have the permission to do so.
- if @show_merge_controls
......
......@@ -29,13 +29,13 @@
%span
%i.icon-remove
Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}.
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
- if @merge_request.merged?
.alert.alert-info
%span
%i.icon-ok
Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}.
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
- if !@closes_issues.empty? && @merge_request.open?
.alert.alert-info.alert-info
%span
......
......@@ -23,7 +23,7 @@
%i.icon-thumbs-up
\+1
- if note.downvote?
%span.vote.downvote.label.label-error
%span.vote.downvote.label.label-danger
%i.icon-thumbs-down
\-1
......
......@@ -9,8 +9,8 @@
.col-md-3.project-side.hidden-sm
.clearfix
- if @project.archived?
.alert
%h5
.alert.alert-warning
%h4
%i.icon-warning-sign
Archived project!
%p Repository is read-only
......
......@@ -19,7 +19,7 @@ module Gitlab
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
config.active_record.observers = :activity_observer,
config.active_record.observers = :milestone_observer,
:project_activity_cache_observer,
:issue_observer,
:key_observer,
......
......@@ -33,6 +33,12 @@ Gitlab::Application.configure do
# See everything in the log (default is :info)
# config.log_level = :debug
# Suppress 'Rendered template ...' messages in the log
# source: http://stackoverflow.com/a/16369363
%w{render_template render_partial render_collection}.each do |event|
ActiveSupport::Notifications.unsubscribe "#{event}.action_view"
end
# Prepend all log lines with the following tags
# config.log_tags = [ :subdomain, :uuid ]
......
# Workaround for https://github.com/github/gemoji/pull/18
require 'gemoji'
Gitlab::Application.config.assets.paths << Emoji.images_path
......@@ -167,7 +167,7 @@ Gitlab::Application.routes.draw do
resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations }
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords}
#
# Project Area
......
worker_processes 2
timeout 30
......@@ -2,7 +2,7 @@ Gitlab::Seeder.quiet do
User.first(30).each_with_index do |user, i|
Key.seed(:id, [
{
id: i,
id: i + 1,
title: "Sample key #{i}",
key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
user_id: user.id,
......
## The GitLab Documentation covers the following subjects
+ [API](api/README.md)
+ [Development](development/README.md)
+ [Install](install/README.md)
+ [Integration](external-issue-tracker/README.md)
+ [Legal](legal/README.md)
+ [Markdown](markdown/markdown.md)
+ [Permissions](permissions/permissions.md)
+ [Public access](public_access/public_access.md)
+ [Raketasks](raketasks/README.md)
+ [Release](release/README.md)
+ [Security](security/README.md)
+ [System hooks](system_hooks/system_hooks.md)
+ [Update](update/README.md)
+ [Web hooks](web_hooks/web_hooks.md)
+ [Workflow](workflow/workflow.md)
# GitLab API
## End-points
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
## Introduction
All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile.
If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401:
......@@ -117,30 +143,3 @@ Issue
So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json`
But when you want to create a link to web page - use `http:://host/project/issues/:iid.json`
## Contents
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
......@@ -150,6 +150,7 @@ Parameters:
+ `target_branch` - The target branch
+ `assignee_id` - Assignee user ID
+ `title` - Title of MR
+ `state_event` - New state (close|reopen|merge)
```json
......@@ -210,3 +211,44 @@ Parameters:
"note":"text1"
}
```
## Get the comments on a MR
Gets all the comments associated with a merge request.
```
GET /projects/:id/merge_request/:merge_request_id/comments
```
Parameters:
+ `id` (required) - The ID of a project
+ `merge_request_id` (required) - ID of merge request
```json
[
{
"note":"this is the 1st comment on the 2merge merge request",
"author":{
"id":11,
"username":"admin",
"email":"admin@local.host",
"name":"Administrator",
"state":"active",
"created_at":"2014-03-06T08:17:35.000Z"
}
},
{
"note":"_Status changed to closed_",
"author":{
"id":11,
"username":"admin",
"email":"admin@local.host",
"name":"Administrator",
"state":"active",
"created_at":"2014-03-06T08:17:35.000Z"
}
}
]
```
......@@ -621,3 +621,29 @@ Parameters:
+ query (required) - A string contained in the project name
+ per_page (optional) - number of projects to return per page
+ page (optional) - the page to retrieve
## Labels
### List project labels
Get a list of project labels.
```
GET /projects/:id/labels
```
Parameters:
+ `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
```json
[
{
"name":"featute"
},
{
"name": "bug"
}
]
```
All methods require admin authorization.
The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
## List system hooks
Get list of system hooks
```
GET /hooks
```
Parameters:
+ **none**
```json
[
{
"id":3,
"url":"http://example.com/hook",
"created_at":"2013-10-02T10:15:31Z"
}
]
```
## Add new system hook hook
```
POST /hooks
```
Parameters:
+ `url` (required) - The hook URL
## Test system hook
```
GET /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
```json
{
"event_name":"project_create",
"name":"Ruby",
"path":"ruby",
"project_id":1,
"owner_name":"Someone",
"owner_email":"example@gitlabhq.com"
}
```
## Delete system hook
Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook
is not available. If the hook is deleted it is also returned as JSON.
```
DELETE /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
All methods require admin authorization.
The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
## List system hooks
Get list of system hooks
```
GET /hooks
```
Parameters:
+ **none**
```json
[
{
"id":3,
"url":"http://example.com/hook",
"created_at":"2013-10-02T10:15:31Z"
}
]
```
## Add new system hook hook
```
POST /hooks
```
Parameters:
+ `url` (required) - The hook URL
## Test system hook
```
GET /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
```json
{
"event_name":"project_create",
"name":"Ruby",
"path":"ruby",
"project_id":1,
"owner_name":"Someone",
"owner_email":"example@gitlabhq.com"
}
```
## Delete system hook
Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook
is not available. If the hook is deleted it is also returned as JSON.
```
DELETE /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
+ [Architecture](architecture.md)
+ [Shell commands](shell_commands.md)
......@@ -18,7 +18,7 @@ New releases are generally around the same time as GitLab CE releases with excep
# System Layout
When referring to ~git in the picures it means the home directory of the git user which is typically /home/git.
When referring to ~git in the pictures it means the home directory of the git user which is typically /home/git.
GitLab is primarily installed within the `/home/git` user home directory as `git` user.
Within the home directory is where the gitlabhq server software resides as well as the repositories (though the repository location is configurable).
......@@ -28,7 +28,7 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
## Components
![GitLab Diagram Overview](resources/gitlab_diagram_overview.png "GitLab Diagram Overview")
![GitLab Diagram Overview](resources/gitlab_diagram_overview.png)
A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS.
It uses Nginx or Apache as a web front end to proxypass the Unicorn web server.
......@@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production
```
Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`.
While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
\ No newline at end of file
# Guidelines for shell commands in the GitLab codebase
## References
- [Google Ruby Security Reviewer's Guide](https://code.google.com/p/ruby-security/wiki/Guide)
- [OWASP Command Injection](https://www.owasp.org/index.php/Command_Injection)
- [Ruby on Rails Security Guide Command Line Injection](http://guides.rubyonrails.org/security.html#command-line-injection)
## Use File and FileUtils instead of shell commands
Sometimes we invoke basic Unix commands via the shell when there is also a Ruby API for doing it.
......
+ [Installation](installation.md)
+ [Requirements](requirements.md)
+ [Structure](structure.md)
+ [Database MySQL](database_mysql.md)
......@@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
# Ensure you have MySQL version 5.5.14 or later
mysql --version
# Pick a database root password (can be anything), type it and press enter
# Retype the database root password and press enter
......@@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# change $password in the command below to a real password you pick
mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
# Ensure you can use the InnoDB engine which is necessary to support long indexes.
# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
mysql> SET storage_engine=INNODB;
# Create the GitLab production database
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
......
......@@ -128,7 +128,7 @@ GitLab Shell is an ssh access and repository management software developed speci
cd /home/git
# Clone gitlab shell
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.8.0
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.9.1
cd gitlab-shell
......@@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
## Clone the Source
# Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-6-stable gitlab
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-7-stable gitlab
# Go to gitlab dir
cd /home/git/gitlab
......
......@@ -5,3 +5,5 @@ GitLab has a great issue tracker but you can also use an external issue tracker
- textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue.
![jira screenshot](jira-intergration-points.png)
You can configure the integration in the gitlab.yml configuration file.
+ [Corporate contributor license agreement](corporate_contributor_license_agreement.md)
+ [Individual contributor license agreement](individual_contributor_license_agreement.md)
......@@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions.
|------|-----|--------|---------|------|-----|
|Browse group|✓|✓|✓|✓|✓|
|Edit group|||||✓|
|create project in group|||||✓|
|Create project in group|||||✓|
|Manage group members|||||✓|
|Remove group|||||✓|
......
+ [Backup restore](backup_restore.md)
+ [Cleanup](cleanup.md)
+ [Features](features.md)
+ [Maintenance](maintenance.md)
+ [User management](user_management.md)
+ [Web hooks](web_hooks.md)
+ [Monthly](monthly.md)
+ [Security](security.md)
......@@ -58,19 +58,17 @@ Check if changed since last release (~22nd of last month depending on when last
After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. The days of the month are approximately as follows:
* 17th: feature freeze (stop merging new features in master)
* 18th: UI freeze (stop merging changes to the user interface)
* 19th: code freeze (stop merging non-essential code improvements)
* 20th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1)
* 21st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
* 1-7th: official merge window (see contributing guide)
* 8-14th: work on bugfixes, sponsored features and GitLab EE
* 15th: code freeze (stop merging into master except essential bugfixes)
* 18th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1, release on GitLab Cloud)
* 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
* 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet)
* 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
* 24-end of month: release Enterprise Edition and upgrade GitLab Cloud
* 1-7th: official merge window (see contributing guide)
* 8-16th: bugfixes and sponsored features
* 24-end of month: release GitLab EE and GitLab CI
# Write a blog post
* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
* Select and thank the the Most Valuable Person (MVP) of this release.
* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
\ No newline at end of file
* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
+ [Password length limits](password_length_limits.md)
+ [Rack attack](rack_attack.md)
Deploy keys allow read-only access one or multiple projects with a single SSH key.
This is really useful for cloning repositories to your Continuous Integration (CI) server.
By using a deploy keys you don't have to setup a dummy user account.
If you are a project master or owner you can add a deploy key in the project settings under the section Deploy Keys.
Press the 'New Deploy Key' button and upload a public ssh key.
After this the machine that uses the corresponding private key has read-only access to the project.
You can't add the same deploy key twice with the 'New Deploy Key' option.
If you want to add the same key to another project please enable it in the list that says 'Deploy keys from projects available to you'.
You need to be the owner of the deploy key to see it in this list.
# From 4.2 to 5.0
## Warning
GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490. Please update to GitLab 5.4 immediately.
GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490.
## Important changes
......
# From 5.0 to 5.1
## Warning
GitLab 5.1 is affected by critical security vulnerability CVE-2013-4490. Please [update to GitLab 5.4 immediately](5.1-to-5.4.md).
GitLab 5.1 is affected by critical security vulnerability CVE-2013-4490.
## Release notes:
......
# From 5.1 to 5.2
## Warning
GitLab 5.2 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 5.4 directly](5.1-to-5.4.md).
GitLab 5.2 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### 0. Backup
......
# From 5.1 to 5.4
Also works starting from 5.2.
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.
### 0. Backup
It's useful to make a backup just in case things go south:
......
# From 5.1 to 6.0
## Warning
GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 immediately](6.0-to-6.2.md).
GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### Deprecations
......
# From 5.2 to 5.3
## Warning
GitLab 5.3 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 5.4 directly](5.1-to-5.4.md).
GitLab 5.3 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### 0. Backup
......
# From 5.3 to 5.4
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.
### 0. Backup
It's useful to make a backup just in case things go south:
......
# From 5.4 to 6.0
## Warning
GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 immediately](6.0-to-6.2.md).
GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
### Deprecations
......
# From 6.0 to 6.1
## Warning
GitLab 6.1 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 directly](6.0-to-6.2.md).
GitLab 6.1 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489.
# In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 so all the necessary conversions are run.
......
# From 6.0 to 6.5
# From 6.0 to 6.7
# In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.
......@@ -28,8 +28,20 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```bash
cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout 6-5-stable
# For GitLab Enterprise Edition: sudo -u git -H git checkout 6-5-stable-ee
```
For Gitlab Community Edition:
```bash
sudo -u git -H git checkout 6-7-stable
```
OR
For GitLab Enterprise Edition:
```bash
sudo -u git -H git checkout 6-7-stable-ee
```
......@@ -45,7 +57,7 @@ sudo apt-get install logrotate
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.8.0 # Addresses multiple critical security vulnerabilities
sudo -u git -H git checkout v1.9.1 # Addresses multiple critical security vulnerabilities
```
### 5. Install libs, migrations, etc.
......@@ -60,11 +72,14 @@ sudo -u git -H bundle install --without development test postgres --deployment
sudo -u git -H bundle install --without development test mysql --deployment
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Enable internal issue IDs (introduced in GitLab 6.1)
sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
```
### 6. Update config files
......@@ -72,11 +87,11 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
TIP: to see what changed in gitlab.yml.example in this release use next command:
```
git diff 6-0-stable:config/gitlab.yml.example 6-5-stable:config/gitlab.yml.example
git diff 6-0-stable:config/gitlab.yml.example 6-7-stable:config/gitlab.yml.example
```
* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-5-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-5-stable/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/unicorn.rb.example but with your settings.
* Copy rack attack middleware config
```bash
......
# From 6.1 to 6.2
## Notice
Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 6.2.
# You should update to 6.1 before installing 6.2 so all the necessary conversions are run.
### 0. Backup
......
......@@ -37,7 +37,7 @@ sudo -u git -H git checkout 6-7-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.8.0
sudo -u git -H git checkout v1.9.1
```
### 4. Install libs, migrations, etc.
......
+ [The indivual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update)
+ [Uprader](upgrader.md)
+ [Ruby](ruby.md)
+ [Patch versions](patch_versions.md)
+ [MySQL to Postgress](mysql_to_postgress.md)
......@@ -5,6 +5,7 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss
You can configure web hook to listen for specific events like pushes, issues, merge requests.
GitLab will send POST request with data to web hook URL.
Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
If you send a web hook to an SSL endpoint [the certificate will not be verified](https://gitlab.com/gitlab-org/gitlab-ce/blob/ccd617e58ea71c42b6b073e692447d0fe3c00be6/app/models/web_hook.rb#L35) since many people use self-signed certificates.
---
......
......@@ -37,6 +37,12 @@ Feature: Project Services
And I fill Assembla settings
Then I should see Assembla service settings saved
Scenario: Activate Slack service
When I visit project "Shop" services page
And I click Slack service link
And I fill Slack settings
Then I should see Slack service settings saved
Scenario: Activate email on push service
When I visit project "Shop" services page
And I click email on push service link
......
......@@ -25,7 +25,7 @@ class Dashboard < Spinach::FeatureSteps
find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New Design"
find("#merge_request_title").value.should == "New design"
end
Given 'user with name "John Doe" joined project "Shop"' do
......
......@@ -4,7 +4,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
include SharedProject
step 'public project "Community"' do
create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
create :project, :public, name: 'Community'
end
step 'private project "Enterprise"' do
......
......@@ -100,4 +100,22 @@ class ProjectServices < Spinach::FeatureSteps
step 'I should see email on push service settings saved' do
find_field('Recipients').value.should == 'qa@company.name'
end
step 'I click Slack service link' do
click_link 'Slack'
end
step 'I fill Slack settings' do
check 'Active'
fill_in 'Subdomain', with: 'gitlab'
fill_in 'Room', with: '#gitlab'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
step 'I should see Slack service settings saved' do
find_field('Subdomain').value.should == 'gitlab'
find_field('Room').value.should == '#gitlab'
find_field('Token').value.should == 'verySecret'
end
end
......@@ -4,7 +4,7 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
include SharedProject
step 'public empty project "Empty Public Project"' do
create :empty_project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC
create :empty_project, :public, name: 'Empty Public Project'
end
step 'I should see project "Empty Public Project"' do
......
......@@ -79,7 +79,7 @@ module SharedProject
end
step 'internal project "Internal"' do
create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
create :project, :internal, name: 'Internal'
end
step 'I should see project "Internal"' do
......@@ -91,7 +91,7 @@ module SharedProject
end
step 'public project "Community"' do
create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
create :project, :public, name: 'Community'
end
step 'I should see project "Community"' do
......@@ -112,14 +112,14 @@ module SharedProject
step '"John Doe" is authorized to internal project "Internal"' do
user = user_exists("John Doe", username: "john_doe")
project = Project.find_by(name: "Internal")
project ||= create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
project ||= create :project, :internal, name: 'Internal'
project.team << [user, :master]
end
step '"John Doe" is authorized to public project "Community"' do
user = user_exists("John Doe", username: "john_doe")
project = Project.find_by(name: "Community")
project ||= create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
project ||= create :project, :public, name: 'Community'
project.team << [user, :master]
end
end
......@@ -187,5 +187,9 @@ module API
end
end
end
class Label < Grape::Entity
expose :name
end
end
end
module API
# Internal access API
class Internal < Grape::API
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
PUSH_COMMANDS = %w{ git-receive-pack }
namespace 'internal' do
#
# Check if ssh key has access to project code
# Check if git command is allowed to project
#
# Params:
# key_id - SSH Key id
# key_id - ssh key id for Git over SSH
# user_id - user id for Git over HTTP
# project - project path with namespace
# action - git action (git-upload-pack or git-receive-pack)
# ref - branch name
......@@ -22,43 +18,25 @@ module API
# the wiki repository as well.
project_path = params[:project]
project_path.gsub!(/\.wiki/,'') if project_path =~ /\.wiki/
key = Key.find(params[:key_id])
project = Project.find_with_namespace(project_path)
git_cmd = params[:action]
return false unless project
if key.is_a? DeployKey
key.projects.include?(project) && DOWNLOAD_COMMANDS.include?(git_cmd)
else
user = key.user
return false if user.blocked?
if Gitlab.config.ldap.enabled
if user.ldap_user?
# Check if LDAP user exists and match LDAP user_filter
unless Gitlab::LDAP::Access.new.allowed?(user)
return false
end
end
end
action = case git_cmd
when *DOWNLOAD_COMMANDS
then :download_code
when *PUSH_COMMANDS
then
if project.protected_branch?(params[:ref])
:push_code_to_protected_branches
else
:push_code
end
end
user.can?(action, project)
end
actor = if params[:key_id]
Key.find(params[:key_id])
elsif params[:user_id]
User.find(params[:user_id])
end
return false unless actor
Gitlab::GitAccess.new.allowed?(
actor,
params[:action],
project,
params[:ref],
params[:oldrev],
params[:newrev]
)
end
#
......
......@@ -125,6 +125,22 @@ module API
end
end
# Get a merge request's comments
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# Examples:
# GET /projects/:id/merge_request/:merge_request_id/comments
#
get ":id/merge_request/:merge_request_id/comments" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present paginate(merge_request.notes), with: Entities::MRNote
end
# Post comment to merge request
#
# Parameters:
......
......@@ -215,6 +215,17 @@ module API
@users = paginate @users
present @users, with: Entities::User
end
# Get a project labels
#
# Parameters:
# id (required) - The ID of a project
# Example Request:
# GET /projects/:id/labels
get ':id/labels' do
@labels = user_project.issues_labels
present @labels, with: Entities::Label
end
end
end
end
require_relative 'shell_env'
require_relative 'grack_helpers'
module Grack
class Auth < Rack::Auth::Basic
include Helpers
attr_accessor :user, :project, :ref, :env
attr_accessor :user, :project, :env
def call(env)
@env = env
......@@ -24,14 +22,16 @@ module Grack
@env['SCRIPT_NAME'] = ""
auth!
if project
auth!
else
render_not_found
end
end
private
def auth!
return render_not_found unless project
if @auth.provided?
return bad_request unless @auth.basic?
......@@ -40,12 +40,8 @@ module Grack
# Allow authentication for GitLab CI service
# if valid token passed
if login == "gitlab-ci-token" && project.gitlab_ci?
token = project.gitlab_ci_service.token
if token.present? && token == password && service_name == 'git-upload-pack'
return @app.call(env)
end
if gitlab_ci_request?(login, password)
return @app.call(env)
end
@user = authenticate_user(login, password)
......@@ -53,23 +49,26 @@ module Grack
if @user
Gitlab::ShellEnv.set_env(@user)
@env['REMOTE_USER'] = @auth.username
else
return unauthorized
end
else
return unauthorized unless project.public?
end
if authorized_git_request?
if authorized_request?
@app.call(env)
else
unauthorized
end
end
def authorized_git_request?
authorize_request(service_name)
def gitlab_ci_request?(login, password)
if login == "gitlab-ci-token" && project.gitlab_ci?
token = project.gitlab_ci_service.token
if token.present? && token == password && git_cmd == 'git-upload-pack'
return true
end
end
false
end
def authenticate_user(login, password)
......@@ -77,31 +76,31 @@ module Grack
auth.find(login, password)
end
def authorize_request(service)
case service
when 'git-upload-pack'
can?(user, :download_code, project)
when'git-receive-pack'
refs.each do |ref|
action = if project.protected_branch?(ref)
:push_code_to_protected_branches
else
:push_code
end
return false unless can?(user, action, project)
def authorized_request?
case git_cmd
when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
if user
Gitlab::GitAccess.new.download_allowed?(user, project)
elsif project.public?
# Allow clone/fetch for public projects
true
else
false
end
when *Gitlab::GitAccess::PUSH_COMMANDS
if user
# Skip user authorization on upload request.
# It will be serverd by update hook in repository
true
else
false
end
# Never let git-receive-pack trough unauthenticated; it's
# harmless but git < 1.8 doesn't like it
return false if user.nil?
true
else
false
end
end
def service_name
def git_cmd
if @request.get?
@request.params['service']
elsif @request.post?
......@@ -115,28 +114,17 @@ module Grack
@project ||= project_by_path(@request.path_info)
end
def refs
@refs ||= parse_refs
end
def parse_refs
input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
Zlib::GzipReader.new(@request.body).read
else
@request.body.read
end
# Need to reset seek point
@request.body.rewind
# Parse refs
refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
def project_by_path(path)
if m = /^([\w\.\/-]+)\.git/.match(path).to_a
path_with_namespace = m.last
path_with_namespace.gsub!(/\.wiki$/, '')
# Cleanup grabare from refs
# if push to multiple branches
refs.map do |ref|
ref.gsub(/00.*/, "")
Project.find_with_namespace(path_with_namespace)
end
end
def render_not_found
[404, {"Content-Type" => "text/plain"}, ["Not Found"]]
end
end
end
module Grack
module Helpers
def project_by_path(path)
if m = /^([\w\.\/-]+)\.git/.match(path).to_a
path_with_namespace = m.last
path_with_namespace.gsub!(/\.wiki$/, '')
Project.find_with_namespace(path_with_namespace)
end
end
def render_not_found
[404, {"Content-Type" => "text/plain"}, ["Not Found"]]
end
def can?(object, action, subject)
abilities.allowed?(object, action, subject)
end
def abilities
@abilities ||= begin
abilities = Six.new
abilities << Ability
abilities
end
end
end
end
module Gitlab
class GitAccess
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
PUSH_COMMANDS = %w{ git-receive-pack }
attr_reader :params, :project, :git_cmd, :user
def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil)
case cmd
when *DOWNLOAD_COMMANDS
if actor.is_a? User
download_allowed?(actor, project)
elsif actor.is_a? DeployKey
actor.projects.include?(project)
elsif actor.is_a? Key
download_allowed?(actor.user, project)
else
raise 'Wrong actor'
end
when *PUSH_COMMANDS
if actor.is_a? User
push_allowed?(actor, project, ref, oldrev, newrev)
elsif actor.is_a? DeployKey
# Deploy key not allowed to push
return false
elsif actor.is_a? Key
push_allowed?(actor.user, project, ref, oldrev, newrev)
else
raise 'Wrong actor'
end
else
false
end
end
def download_allowed?(user, project)
if user && user_allowed?(user)
user.can?(:download_code, project)
else
false
end
end
def push_allowed?(user, project, ref, oldrev, newrev)
if user && user_allowed?(user)
action = if project.protected_branch?(ref)
:push_code_to_protected_branches
else
:push_code
end
user.can?(action, project)
else
false
end
end
private
def user_allowed?(user)
return false if user.blocked?
if Gitlab.config.ldap.enabled
if user.ldap_user?
# Check if LDAP user exists and match LDAP user_filter
unless Gitlab::LDAP::Access.new.allowed?(user)
return false
end
end
end
true
end
end
end
......@@ -152,7 +152,7 @@ module Gitlab
#
# Returns boolean
def valid_emoji?(emoji)
Emoji.names.include? emoji
Emoji.find_by_name emoji
end
# Private: Dispatches to a dedicated processing method based on reference
......@@ -166,8 +166,8 @@ module Gitlab
end
def reference_user(identifier)
if member = @project.team_members.find { |user| user.username == identifier }
link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member
if user = User.find_by_username(identifier)
link_to("@#{identifier}", user_url(identifier), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}"))
end
end
......
module Gitlab
class SatelliteNotExistError < StandardError; end
class SatelliteNotExistError < StandardError
def initialize(msg = "Satellite doesn't exist")
super
end
end
module Satellite
class Satellite
......@@ -17,14 +21,9 @@ module Gitlab
Gitlab::Satellite::Logger.error(message)
end
def raise_no_satellite
raise SatelliteNotExistError.new("Satellite doesn't exist")
end
def clear_and_update!
raise_no_satellite unless exists?
raise SatelliteNotExistError unless exists?
File.exists? path
@repo = nil
clear_working_dir!
delete_heads!
......@@ -55,7 +54,7 @@ module Gitlab
# * Changes the current directory to the satellite's working dir
# * Yields
def lock
raise_no_satellite unless exists?
raise SatelliteNotExistError unless exists?
File.open(lock_file, "w+") do |f|
begin
......@@ -77,7 +76,7 @@ module Gitlab
end
def repo
raise_no_satellite unless exists?
raise SatelliteNotExistError unless exists?
@repo ||= Grit::Repo.new(path)
end
......
require_relative "popen"
require_relative "version_info"
module Gitlab
......
......@@ -40,7 +40,7 @@ test -f /etc/default/gitlab && . /etc/default/gitlab
# Switch to the app_user if it is not he/she who is running the script.
if [ "$USER" != "$app_user" ]; then
sudo -u "$app_user" -H -i $0 "$@"; exit;
eval su - "$app_user" -c $(echo \")$0 "$@"$(echo \"); exit;
fi
# Switch to the gitlab path, exit on failure.
......@@ -131,7 +131,7 @@ check_stale_pids(){
fi
fi
if [ "$spid" != "0" -a "$sidekiq_status" != "0" ]; then
echo "Removing stale Sidekiq web server pid. This is most likely caused by the Sidekiq crashing the last time it ran."
echo "Removing stale Sidekiq job dispatcher pid. This is most likely caused by Sidekiq crashing the last time it ran."
if ! rm "$sidekiq_pid_path"; then
echo "Unable to remove stale pid, exiting"
exit 1
......@@ -149,15 +149,15 @@ exit_if_not_running(){
}
## Starts Unicorn and Sidekiq if they're not running.
start() {
start_gitlab() {
check_stale_pids
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
echo -n "Starting both the GitLab Unicorn and Sidekiq"
elif [ "$web_status" != "0" ]; then
echo -n "Starting GitLab Sidekiq"
elif [ "$sidekiq_status" != "0" ]; then
echo -n "Starting GitLab Unicorn"
elif [ "$sidekiq_status" != "0" ]; then
echo -n "Starting GitLab Sidekiq"
fi
# Then check if the service is running. If it is: don't start again.
......@@ -167,7 +167,7 @@ start() {
# Remove old socket if it exists
rm -f "$socket_path"/gitlab.socket 2>/dev/null
# Start the web server
RAILS_ENV=$RAILS_ENV script/web start &
RAILS_ENV=$RAILS_ENV script/web start
fi
# If sidekiq is already running, don't start it again.
......@@ -184,15 +184,15 @@ start() {
}
## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them.
stop() {
stop_gitlab() {
exit_if_not_running
if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
echo -n "Shutting down both Unicorn and Sidekiq"
elif [ "$web_status" = "0" ]; then
echo -n "Shutting down Sidekiq"
elif [ "$sidekiq_status" = "0" ]; then
echo -n "Shutting down Unicorn"
elif [ "$sidekiq_status" = "0" ]; then
echo -n "Shutting down Sidekiq"
fi
# If the Unicorn web server is running, tell it to stop;
......@@ -246,7 +246,7 @@ print_status() {
}
## Tells unicorn to reload it's config and Sidekiq to restart
reload(){
reload_gitlab(){
exit_if_not_running
if [ "$wpid" = "0" ];then
echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded."
......@@ -263,12 +263,12 @@ reload(){
}
## Restarts Sidekiq and Unicorn.
restart(){
restart_gitlab(){
check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
stop
stop_gitlab
fi
start
start_gitlab
}
......@@ -276,16 +276,16 @@ restart(){
case "$1" in
start)
start
start_gitlab
;;
stop)
stop
stop_gitlab
;;
restart)
restart
restart_gitlab
;;
reload|force-reload)
reload
reload_gitlab
;;
status)
print_status
......
......@@ -54,6 +54,14 @@ server {
proxy_pass http://gitlab;
}
# Enable gzip compression as per rails guide: http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
location ~ ^/(assets)/ {
root /home/git/gitlab/public;
gzip_static on; # to serve pre-gzipped version
expires max;
add_header Cache-Control public;
}
error_page 502 /502.html;
}
......@@ -677,7 +677,20 @@ namespace :gitlab do
end
def filter
Net::LDAP::Filter.present?(ldap_config.uid)
uid_filter = Net::LDAP::Filter.present?(ldap_config.uid)
if user_filter
Net::LDAP::Filter.join(uid_filter, user_filter)
else
uid_filter
end
end
def user_filter
if ldap_config['user_filter'] && ldap_config.user_filter.present?
Net::LDAP::Filter.construct(ldap_config.user_filter)
else
nil
end
end
def ldap
......@@ -742,7 +755,7 @@ namespace :gitlab do
end
def check_gitlab_shell
required_version = Gitlab::VersionInfo.new(1, 8, 5)
required_version = Gitlab::VersionInfo.new(1, 9, 1)
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_version} ? ... "
......
......@@ -6,6 +6,11 @@ sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid"
sidekiq_logfile="$app_root/log/sidekiq.log"
gitlab_user=$(ls -l config.ru | awk '{print $3}')
function warn
{
echo "$@" 1>&2
}
function stop
{
bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1
......@@ -35,6 +40,22 @@ function start_sidekiq
bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
}
function load_ok
{
sidekiq_pid=$(cat $sidekiq_pidfile)
if [[ -z $sidekiq_pid ]] ; then
warn "Could not find a PID in $sidekiq_pidfile"
exit 0
fi
if (ps -p $sidekiq_pid -o args | grep '\([0-9]\+\) of \1 busy' 1>&2) ; then
warn "Too many busy Sidekiq workers"
exit 1
fi
exit 0
}
case "$1" in
stop)
stop
......@@ -51,6 +72,9 @@ case "$1" in
killall)
killall
;;
load_ok)
load_ok
;;
*)
echo "Usage: RAILS_ENV=your_env $0 {stop|start|start_no_deamonize|restart|killall}"
echo "Usage: RAILS_ENV=your_env $0 {stop|start|start_no_deamonize|restart|killall|load_ok}"
esac
......@@ -24,6 +24,11 @@ describe Profiles::KeysController do
expect(response.body).to eq("")
end
it "should respond with text/plain content type" do
get :get_keys, username: user.username
expect(response.content_type).to eq("text/plain")
end
end
describe "user with keys" do
......@@ -44,6 +49,11 @@ describe Profiles::KeysController do
expect(response.body).not_to eq("")
expect(response.body).to eq(user.all_ssh_keys.join("\n"))
end
it "should respond with text/plain content type" do
get :get_keys, username: user.username
expect(response.content_type).to eq("text/plain")
end
end
end
end
......@@ -32,6 +32,18 @@ FactoryGirl.define do
path { name.downcase.gsub(/\s/, '_') }
namespace
creator
trait :public do
visibility_level Gitlab::VisibilityLevel::PUBLIC
end
trait :internal do
visibility_level Gitlab::VisibilityLevel::INTERNAL
end
trait :private do
visibility_level Gitlab::VisibilityLevel::PRIVATE
end
end
# Generates a test repository from the repository stored under `spec/seed_project.tar.gz`.
......@@ -146,6 +158,11 @@ FactoryGirl.define do
state :reopened
end
trait :simple do
source_branch "simple_merge_request"
target_branch "master"
end
factory :closed_merge_request, traits: [:closed]
factory :reopened_merge_request, traits: [:reopened]
factory :merge_request_with_diffs, traits: [:with_diffs]
......@@ -161,7 +178,6 @@ FactoryGirl.define do
factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
factory :note_on_merge_request, traits: [:on_merge_request]
factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
factory :note_on_merge_request_with_attachment, traits: [:on_merge_request, :with_attachment]
trait :on_commit do
project factory: :project
......
require 'spec_helper'
describe "On a merge request", js: true do
let!(:project) { create(:project) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let!(:note) { create(:note_on_merge_request_with_attachment, project: project) }
let!(:merge_request) { create(:merge_request, :simple) }
let!(:project) { merge_request.source_project }
let!(:note) { create(:note_on_merge_request, :with_attachment, project: project) }
before do
login_as :user
project.team << [@user, :master]
login_as :admin
visit project_merge_request_path(project, merge_request)
end
......@@ -134,22 +132,20 @@ describe "On a merge request", js: true do
end
end
describe "On a merge request diff", js: true, focus: true do
let!(:project) { create(:project) }
let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) }
describe "On a merge request diff", js: true do
let(:merge_request) { create(:merge_request, :with_diffs, :simple) }
let(:project) { merge_request.source_project }
before do
login_as :user
project.team << [@user, :master]
login_as :admin
visit diffs_project_merge_request_path(project, merge_request)
end
subject { page }
describe "when adding a note" do
before do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click
find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
end
describe "the notes holder" do
......@@ -160,13 +156,13 @@ describe "On a merge request diff", js: true, focus: true do
describe "the note form" do
it "shouldn't add a second form for same row" do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click
find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
should have_css("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185'] + .js-temp-notes-holder form", count: 1)
should have_css("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7'] + .js-temp-notes-holder form", count: 1)
end
it "should be removed when canceled" do
within(".diff-file form[rel$='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185']") do
within(".diff-file form[rel$='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7']") do
find(".js-close-discussion-note-form").trigger("click")
end
......@@ -176,12 +172,9 @@ describe "On a merge request diff", js: true, focus: true do
end
describe "with muliple note forms" do
let!(:project) { create(:project) }
let!(:merge_request) { create(:merge_request_with_diffs, source_project: project, target_project: project) }
before do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185"]').click
find('a[data-line-code="342e16cbbd482ac2047dc679b2749d248cc1428f_18_17"]').click
find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7"]').click
find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10"]').click
end
it { should have_css(".js-temp-notes-holder", count: 2) }
......@@ -189,12 +182,12 @@ describe "On a merge request diff", js: true, focus: true do
describe "previewing them separately" do
before do
# add two separate texts and trigger previews on both
within("tr[id='4735dfc552ad7bf15ca468adc3cad9d05b624490_172_185'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "One comment on line 185"
within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_7_7'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "One comment on line 7"
find(".js-note-preview-button").trigger("click")
end
within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "Another comment on line 17"
within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "Another comment on line 10"
find(".js-note-preview-button").trigger("click")
end
end
......@@ -202,14 +195,14 @@ describe "On a merge request diff", js: true, focus: true do
describe "posting a note" do
before do
within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "Another comment on line 17"
within("tr[id='8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_10_10'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "Another comment on line 10"
click_button("Add Comment")
end
end
it 'should be added as discussion' do
should have_content("Another comment on line 17")
should have_content("Another comment on line 10")
should have_css(".notes_holder")
should have_css(".notes_holder .note", count: 1)
should have_link("Reply")
......
......@@ -16,7 +16,7 @@ describe "Group with internal project access" do
group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST)
create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
create(:project, :internal, group: group)
end
describe "GET /groups/:path" do
......
......@@ -16,8 +16,8 @@ describe "Group access" do
group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST)
create(:project, path: "internal_project", group: group, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
create(:project, path: "public_project", group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
create(:project, :internal, path: "internal_project", group: group)
create(:project, :public, path: "public_project", group: group)
end
describe "GET /groups/:path" do
......
......@@ -16,7 +16,7 @@ describe "Group with public project access" do
group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST)
create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
create(:project, :public, group: group)
end
describe "GET /groups/:path" do
......
require 'spec_helper'
describe "Internal Project Access" do
let(:project) { create(:project) }
let(:project) { create(:project, :internal) }
let(:master) { create(:user) }
let(:guest) { create(:user) }
let(:reporter) { create(:user) }
before do
# internal project
project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
project.save!
# full access
project.team << [master, :master]
# readonly
project.team << [reporter, :reporter]
end
describe "Project should be internal" do
......
require 'spec_helper'
describe MergeRequestsFinder do
let(:user) { create :user }
let(:user) { create :user }
let(:user2) { create :user }
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:merge_request1) { create(:merge_request, author: user, source_project: project1, target_project: project2) }
let(:merge_request2) { create(:merge_request, author: user, source_project: project2, target_project: project1) }
let(:merge_request3) { create(:merge_request, author: user, source_project: project2, target_project: project2) }
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2) }
let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
before do
project1.team << [user, :master]
......@@ -15,13 +17,7 @@ describe MergeRequestsFinder do
project2.team << [user2, :developer]
end
describe :execute do
before :each do
merge_request1
merge_request2
merge_request3
end
describe "#execute" do
it 'should filter by scope' do
params = { scope: 'authored', state: 'opened' }
merge_requests = MergeRequestsFinder.new.execute(user, params)
......
......@@ -4,10 +4,10 @@ describe ProjectsFinder do
let(:user) { create :user }
let(:group) { create :group }
let(:project1) { create(:empty_project, group: group, visibility_level: Project::PUBLIC) }
let(:project2) { create(:empty_project, group: group, visibility_level: Project::INTERNAL) }
let(:project3) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) }
let(:project4) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) }
let(:project1) { create(:empty_project, :public, group: group) }
let(:project2) { create(:empty_project, :internal, group: group) }
let(:project3) { create(:empty_project, :private, group: group) }
let(:project4) { create(:empty_project, :private, group: group) }
context 'non authenticated' do
subject { ProjectsFinder.new.execute(nil, group: group) }
......
......@@ -229,6 +229,7 @@ describe Notify do
end
context 'for merge requests' do
let(:merge_author) { create(:user) }
let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) }
......@@ -288,7 +289,30 @@ describe Notify do
it 'contains a link to the merge request' do
should have_body_text /#{project_merge_request_path project, merge_request}/
end
end
describe 'that are merged' do
subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) }
it_behaves_like 'a multiple recipients email'
it 'is sent as the merge author' do
sender = subject.header[:from].addrs[0]
sender.display_name.should eq(merge_author.name)
sender.address.should eq(gitlab_sender)
end
it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/
end
it 'contains the new status' do
should have_body_text /merged/i
end
it 'contains a link to the merge request' do
should have_body_text /#{project_merge_request_path project, merge_request}/
end
end
end
end
......
......@@ -25,11 +25,6 @@ describe Issue, "Issuable" do
it { described_class.should respond_to(:assigned) }
end
it "has an :author_id_of_changes accessor" do
issue.should respond_to(:author_id_of_changes)
issue.should respond_to(:author_id_of_changes=)
end
describe ".search" do
let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
......
......@@ -47,6 +47,7 @@ describe Project do
it { should have_many(:hooks).dependent(:destroy) }
it { should have_many(:protected_branches).dependent(:destroy) }
it { should have_one(:forked_project_link).dependent(:destroy) }
it { should have_one(:slack_service).dependent(:destroy) }
end
describe "Mass assignment" do
......
require_relative '../../app/models/project_services/slack_message'
describe SlackMessage do
subject { SlackMessage.new(args) }
let(:args) {
{
after: 'after',
before: 'before',
project_name: 'project_name',
ref: 'refs/heads/master',
user_name: 'user_name',
project_url: 'url'
}
}
context 'push' do
before do
args[:commits] = [
{ message: 'message1', url: 'url1', id: 'abcdefghi' },
{ message: 'message2', url: 'url2', id: '123456789' },
]
end
it 'returns a message regarding pushes' do
subject.compose.should ==
'user_name pushed to branch <url/commits/master|master> of ' <<
'<url|project_name> (<url/compare/before...after|Compare changes>)' <<
"\n - message1 (<url1|abcdef>)" <<
"\n - message2 (<url2|123456>)"
end
end
context 'new branch' do
before do
args[:before] = '000000'
end
it 'returns a message regarding a new branch' do
subject.compose.should ==
'user_name pushed new branch <url/commits/master|master> to ' <<
'<url|project_name>'
end
end
context 'removed branch' do
before do
args[:after] = '000000'
end
it 'returns a message regarding a removed branch' do
subject.compose.should ==
'user_name removed branch master from <url|project_name>'
end
end
end
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# token :string(255)
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
# api_key :string(255)
#
require 'spec_helper'
describe SlackService do
describe "Associations" do
it { should belong_to :project }
it { should have_one :service_hook }
end
describe "Validations" do
context "active" do
before do
subject.active = true
end
it { should validate_presence_of :room }
it { should validate_presence_of :subdomain }
it { should validate_presence_of :token }
end
end
describe "Execute" do
let(:slack) { SlackService.new }
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:sample_data) { GitPushService.new.sample_data(project, user) }
let(:subdomain) { 'gitlab' }
let(:token) { 'verySecret' }
let(:api_url) {
"https://#{subdomain}.slack.com/services/hooks/incoming-webhook?token=#{token}"
}
before do
slack.stub(
project: project,
project_id: project.id,
room: '#gitlab',
service_hook: true,
subdomain: subdomain,
token: token
)
WebMock.stub_request(:post, api_url)
end
it "should call Slack API" do
slack.execute(sample_data)
WebMock.should have_requested(:post, api_url).once
end
end
end
......@@ -292,6 +292,20 @@ describe User do
end
end
describe 'search' do
let(:user1) { create(:user, username: 'James', email: 'james@testing.com') }
let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') }
it "should be case insensitive" do
User.search(user1.username.upcase).to_a.should == [user1]
User.search(user1.username.downcase).to_a.should == [user1]
User.search(user2.username.upcase).to_a.should == [user2]
User.search(user2.username.downcase).to_a.should == [user2]
User.search(user1.username.downcase).to_a.count.should == 2
User.search(user2.username.downcase).to_a.count.should == 1
end
end
describe 'by_username_or_id' do
let(:user1) { create(:user, username: 'foo') }
......
require 'spec_helper'
describe ActivityObserver do
let(:project) { create(:project) }
before { Thread.current[:current_user] = create(:user) }
def self.it_should_be_valid_event
it { @event.should_not be_nil }
it { @event.project.should == project }
end
describe "Issue created" do
before do
Issue.observers.enable :activity_observer do
@issue = create(:issue, project: project)
@event = Event.last
end
end
it_should_be_valid_event
it { @event.action.should == Event::CREATED }
it { @event.target.should == @issue }
end
describe "Issue commented" do
before do
Note.observers.enable :activity_observer do
@issue = create(:issue, project: project)
@note = create(:note, noteable: @issue, project: project, author: @issue.author)
@event = Event.last
end
end
it_should_be_valid_event
it { @event.action.should == Event::COMMENTED }
it { @event.target.should == @note }
end
describe "Ignore system notes" do
let(:author) { create(:user) }
let!(:issue) { create(:issue, project: project) }
let!(:other) { create(:issue) }
it "should not create events for status change notes" do
expect do
Note.observers.enable :activity_observer do
Note.create_status_change_note(issue, project, author, 'reopened', nil)
end
end.to_not change { Event.count }
end
it "should not create events for cross-reference notes" do
expect do
Note.observers.enable :activity_observer do
Note.create_cross_reference_note(issue, other, author, issue.project)
end
end.to_not change { Event.count }
end
end
end
......@@ -120,7 +120,7 @@ describe MergeRequestObserver do
end
before do
@merge_request = create(:merge_request, source_project: project, source_project: project)
@merge_request = create(:merge_request, source_project: project, target_project: project)
@event = Event.last
end
......
......@@ -100,16 +100,4 @@ describe API::API do
response.status.should == 405
end
end
describe "PUT /projects/:id/issues/:issue_id to test observer on close" do
before { enable_observers }
after { disable_observers }
it "should create an activity event when an issue is closed" do
Event.should_receive(:create)
put api("/projects/#{project.id}/issues/#{issue.id}", user),
state_event: "close"
end
end
end
......@@ -7,6 +7,7 @@ describe API::API do
let(:user) { create(:user) }
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
before {
project.team << [user, :reporters]
}
......@@ -205,4 +206,20 @@ describe API::API do
response.status.should == 404
end
end
describe "GET :id/merge_request/:merge_request_id/comments" do
it "should return merge_request comments" do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['note'].should == "a comment on a MR"
json_response.first['author']['id'].should == user.id
end
it "should return a 404 error if merge_request_id not found" do
get api("/projects/#{project.id}/merge_request/999/comments", user)
response.status.should == 404
end
end
end
......@@ -13,6 +13,7 @@ describe API::API do
let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
let(:issue_with_labels) { create(:issue, author: user, assignee: user, project: project, :label_list => "label1, label2") }
describe "GET /projects" do
before { project }
......@@ -133,7 +134,7 @@ describe API::API do
end
it "should set a project as public" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
project = attributes_for(:project, :public)
post api("/projects", user), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
......@@ -147,21 +148,21 @@ describe API::API do
end
it "should set a project as internal" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
project = attributes_for(:project, :internal)
post api("/projects", user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
it "should set a project as internal overriding :public" do
project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
project = attributes_for(:project, :internal, { public: true })
post api("/projects", user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
it "should set a project as private" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
project = attributes_for(:project, :private)
post api("/projects", user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
......@@ -215,7 +216,7 @@ describe API::API do
end
it "should set a project as public" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
project = attributes_for(:project, :public)
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
......@@ -229,21 +230,21 @@ describe API::API do
end
it "should set a project as internal" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
project = attributes_for(:project, :internal)
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
it "should set a project as internal overriding :public" do
project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
project = attributes_for(:project, :internal, { public: true })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
it "should set a project as private" do
project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
project = attributes_for(:project, :private)
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
......@@ -490,10 +491,10 @@ describe API::API do
describe :fork_admin do
let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:project_fork_source) { create(:project, :public) }
describe "POST /projects/:id/fork/:forked_from_id" do
let(:new_project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let(:new_project_fork_source) { create(:project, :public) }
it "shouldn't available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
......@@ -562,10 +563,10 @@ describe API::API do
let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
let!(:internal) { create(:empty_project, name: "internal #{query}", visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
let!(:unfound_internal) { create(:empty_project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
let!(:public) { create(:empty_project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let!(:unfound_public) { create(:empty_project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let!(:internal) { create(:empty_project, :internal, name: "internal #{query}") }
let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
let!(:public) { create(:empty_project, :public, name: "public #{query}") }
let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
context "when unauthenticated" do
it "should return authentication error" do
......@@ -632,4 +633,16 @@ describe API::API do
end
end
end
describe "GET /projects/:id/labels" do
before { issue_with_labels }
it "should return project labels" do
get api("/projects/#{project.id}/labels", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['name'].should == issue_with_labels.labels.first.name
json_response.last['name'].should == issue_with_labels.labels.last.name
end
end
end
No preview for this file type
require 'spec_helper'
describe EventCreateService do
let(:service) { EventCreateService.new }
describe 'Issues' do
describe :open_issue do
let(:issue) { create(:issue) }
it { service.open_issue(issue, issue.author).should be_true }
it "should create new event" do
expect { service.open_issue(issue, issue.author) }.to change { Event.count }
end
end
describe :close_issue do
let(:issue) { create(:issue) }
it { service.close_issue(issue, issue.author).should be_true }
it "should create new event" do
expect { service.close_issue(issue, issue.author) }.to change { Event.count }
end
end
describe :reopen_issue do
let(:issue) { create(:issue) }
it { service.reopen_issue(issue, issue.author).should be_true }
it "should create new event" do
expect { service.reopen_issue(issue, issue.author) }.to change { Event.count }
end
end
end
describe 'Merge Requests' do
describe :open_mr do
let(:merge_request) { create(:merge_request) }
it { service.open_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.open_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
describe :close_mr do
let(:merge_request) { create(:merge_request) }
it { service.close_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.close_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
describe :merge_mr do
let(:merge_request) { create(:merge_request) }
it { service.merge_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.merge_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
describe :reopen_mr do
let(:merge_request) { create(:merge_request) }
it { service.reopen_mr(merge_request, merge_request.author).should be_true }
it "should create new event" do
expect { service.reopen_mr(merge_request, merge_request.author) }.to change { Event.count }
end
end
end
describe 'Milestone' do
let(:user) { create :user }
describe :open_milestone do
let(:milestone) { create(:milestone) }
it { service.open_milestone(milestone, user).should be_true }
it "should create new event" do
expect { service.open_milestone(milestone, user) }.to change { Event.count }
end
end
describe :close_mr do
let(:milestone) { create(:milestone) }
it { service.close_milestone(milestone, user).should be_true }
it "should create new event" do
expect { service.close_milestone(milestone, user) }.to change { Event.count }
end
end
end
end
......@@ -32,6 +32,7 @@ describe NotificationService do
describe 'Notes' do
context 'issue note' do
let(:issue) { create(:issue, assignee: create(:user)) }
let(:mentioned_issue) { create(:issue, assignee: issue.assignee) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') }
before do
......@@ -50,6 +51,13 @@ describe NotificationService do
notification.new_note(note)
end
it 'filters out "mentioned in" notes' do
mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
Notify.should_not_receive(:note_issue_email)
notification.new_note(mentioned_note)
end
def should_email(user_id)
Notify.should_receive(:note_issue_email).with(user_id, note.id)
end
......@@ -233,15 +241,15 @@ describe NotificationService do
should_email(@u_watcher.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
notification.merge_mr(merge_request)
notification.merge_mr(merge_request, @u_disabled)
end
def should_email(user_id)
Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id)
Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end
def should_not_email(user_id)
Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id)
Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
end
end
end
......
require 'spec_helper'
describe 'Search::GlobalService' do
let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
let(:user) { create(:user, namespace: found_namespace) }
let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
let(:public_user) { create(:user, namespace: public_namespace) }
let(:internal_user) { create(:user, namespace: internal_namespace) }
let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
let(:internal_namespace) { create(:namespace, name: 'searchable internal namespace', path: 'something_internal') }
let(:public_namespace) { create(:namespace, name: 'searchable public namespace', path: 'something_public') }
let(:internal_namespace) { create(:namespace, path: 'something_internal',name: 'searchable internal namespace') }
let(:internal_user) { create(:user, namespace: internal_namespace) }
let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
let(:public_namespace) { create(:namespace, path: 'something_public',name: 'searchable public namespace') }
let(:public_user) { create(:user, namespace: public_namespace) }
let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
let!(:found_project) { create(:project, :private, name: 'searchable_project', creator_id: user.id, namespace: found_namespace) }
let!(:unfound_project) { create(:project, :private, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace) }
let!(:internal_project) { create(:project, :internal, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace) }
let!(:public_project) { create(:project, :public, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace) }
describe '#execute' do
context 'unauthenticated' do
it 'should return public projects only' do
context = Search::GlobalService.new(nil, search: "searchable")
results = context.execute
results[:projects].should have(1).items
results[:projects].should include(public_project)
results[:projects].should match_array [public_project]
end
end
......@@ -30,24 +28,19 @@ describe 'Search::GlobalService' do
it 'should return public, internal and private projects' do
context = Search::GlobalService.new(user, search: "searchable")
results = context.execute
results[:projects].should have(3).items
results[:projects].should include(public_project)
results[:projects].should include(found_project)
results[:projects].should include(internal_project)
results[:projects].should match_array [public_project, found_project, internal_project]
end
it 'should return only public & internal projects' do
context = Search::GlobalService.new(internal_user, search: "searchable")
results = context.execute
results[:projects].should have(2).items
results[:projects].should include(internal_project)
results[:projects].should include(public_project)
results[:projects].should match_array [internal_project, public_project]
end
it 'namespace name should be searchable' do
context = Search::GlobalService.new(user, search: "searchable namespace")
results = context.execute
results[:projects].should == [found_project]
results[:projects].should match_array [found_project]
end
end
end
......
require 'rubygems'
require 'spork'
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
Spork.prefork do
require 'simplecov' unless ENV['CI']
require 'simplecov' unless ENV['CI']
if ENV['TRAVIS']
require 'coveralls'
Coveralls.wear!
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
require 'webmock/rspec'
require 'email_spec'
require 'sidekiq/testing/inline'
require 'capybara/poltergeist'
# Loading more in this block will cause your tests to run faster. However,
# if you change any configuration or code from libraries loaded here, you'll
# need to restart spork for it take effect.
Capybara.javascript_driver = :poltergeist
Capybara.default_wait_time = 10
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config|
config.mock_with :rspec
config.include LoginHelpers, type: :feature
config.include LoginHelpers, type: :request
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
config.include TestEnv
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
config.before(:suite) do
TestEnv.init(observers: false, init_repos: true, repos: false)
end
config.before(:each) do
TestEnv.setup_stubs
end
end
if ENV['TRAVIS']
require 'coveralls'
Coveralls.wear!
end
Spork.each_run do
# This code will be run each time you run your specs.
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
require 'webmock/rspec'
require 'email_spec'
require 'sidekiq/testing/inline'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
Capybara.default_wait_time = 10
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config|
config.mock_with :rspec
config.include LoginHelpers, type: :feature
config.include LoginHelpers, type: :request
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
config.include TestEnv
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
config.before(:suite) do
TestEnv.init(observers: false, init_repos: true, repos: false)
end
config.before(:each) do
TestEnv.setup_stubs
end
end
......@@ -29,7 +29,6 @@ module TestEnv
disable_mailer if opts[:mailer] == false
setup_stubs
clear_test_repo_dir if opts[:init_repos] == true
setup_test_repos(opts) if opts[:repos] == true
end
......@@ -91,7 +90,7 @@ module TestEnv
size: 12.45
)
ActivityObserver.any_instance.stub(
BaseObserver.any_instance.stub(
current_user: double("current_user", id: 1)
)
end
......@@ -165,8 +164,7 @@ module TestEnv
def clear_test_repo_dir
setup_stubs
# Use tmp dir for FS manipulations
repos_path = testing_path()
# Remove tmp/test-git-base-path
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
......
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