Commit b2e74741 authored by dosire's avatar dosire

Merge branch 'master' into define-feature-request

Conflicts:
	PROCESS.md
parents 775f686e 7f67e066
--color --drb
--color
......@@ -9,7 +9,40 @@ 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)
- Fixes a bug with group member administration (Matt DeTullio)
- Sort tag names using VersionSorter (Robert Speicher)
- Add GFM autocompletion for MergeRequests (Robert Speicher)
- Add webhook when a new tag is pushed (Jeroen van Baarsen)
- Add button for toggling inline comments in diff view
- Add retry feature for repository import
- Reuse the GitLab LDAP connection within each request
- Changed markdown new line behaviour to conform to markdown standards
- Fix global search
- 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)
- Hide mr close button for comment form if merge request was closed or inline comment
- Adds ability to reopen closed merge request
v 6.6.4
- Add missing html escape for highlighted code blocks in comments, issues
v 6.6.3
- Fix 500 error when edit yourself from admin area
- Hide private groups for public profiles
v 6.6.2
- Fix 500 error on branch/tag create or remove via UI
......
......@@ -22,11 +22,14 @@ Issues and merge requests should be in English and contain appropriate language
## Issue tracker
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). 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/).
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 bugs in the latest [stable or development release of GitLab](MAINTENANCE.md).
If something is missing 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,7 @@ 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. 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 +77,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)
......
......@@ -48,7 +48,8 @@ gem "gitlab-linguist", "~> 3.0.0", require: "linguist"
# API
gem "grape", "~> 0.6.1"
gem "grape-entity", "~> 0.3.0"
# Replace with rubygems when nesteted entities get released
gem "grape-entity", "~> 0.4.1", ref: 'd904381c951e86250c3f44213b349a3dd8e83fb1', git: 'https://github.com/intridea/grape-entity.git'
gem 'rack-cors', require: 'rack/cors'
# Email validation
......@@ -131,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"
......@@ -161,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"
......@@ -213,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'
......
......@@ -5,6 +5,15 @@ GIT
specs:
github-markup (0.7.6)
GIT
remote: https://github.com/intridea/grape-entity.git
revision: d904381c951e86250c3f44213b349a3dd8e83fb1
ref: d904381c951e86250c3f44213b349a3dd8e83fb1
specs:
grape-entity (0.4.1)
activesupport
multi_json (>= 1.3.2)
GEM
remote: https://rubygems.org/
specs:
......@@ -119,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)
......@@ -156,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)
......@@ -181,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)
......@@ -206,9 +218,6 @@ GEM
rack-accept
rack-mount
virtus (>= 1.0.0)
grape-entity (0.3.0)
activesupport
multi_json (>= 1.3.2)
growl (1.0.3)
guard (2.2.4)
formatador (>= 0.2.4)
......@@ -235,7 +244,7 @@ GEM
httparty
httparty
http_parser.rb (0.5.3)
httparty (0.12.0)
httparty (0.13.0)
json (~> 1.8)
multi_xml (>= 0.5.2)
httpauth (0.2.0)
......@@ -291,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)
......@@ -462,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)
......@@ -473,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)
......@@ -585,18 +595,18 @@ 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)
gon (~> 5.0.0)
grape (~> 0.6.1)
grape-entity (~> 0.3.0)
grape-entity (~> 0.4.1)!
growl
guard-rspec
guard-spinach
......@@ -614,6 +624,7 @@ DEPENDENCIES
minitest (~> 4.7.0)
modernizr (= 2.6.2)
mysql2
nprogress-rails
omniauth (~> 1.1.3)
omniauth-github
omniauth-google-oauth2
......@@ -646,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)
......
......@@ -12,7 +12,7 @@ Below we describe the contributing process to GitLab for two reasons. So that co
- Closes invalid issues and merge requests with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.)
- Assigns appropriate [labels](#how-we-handle-issues)
- Asks for feedback from issue reporter/merge request initiator ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.)
- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](http://gitlab.org/team/)
- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](https://www.gitlab.com/core-team/)
- Monitors all issues/merge requests for feedback (but especially ones commented on since automatically watching them):
- Closes issues with no feedback from the reporter for two weeks
- Closes stale merge requests
......@@ -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
......@@ -37,7 +35,7 @@ Below we describe the contributing process to GitLab for two reasons. So that co
## Mentioning people
The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](http://gitlab.org/team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person.
The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](https://www.gitlab.com/core-team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person.
## Workflow labels
......@@ -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. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. 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
......@@ -89,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea
### Issue fixed in newer version
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(http://blog.gitlab.org/). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Improperly formatted merge request
......
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
......@@ -23,13 +23,11 @@
### Resources
* GitLab.org community site: [Homepage](http://gitlab.org) | [Screenshots](http://gitlab.org/screenshots/) | [Blog](http://blog.gitlab.org/) | [Demo](http://demo.gitlabhq.com/users/sign_in)
* [GitLab.com](https://www.gitlab.com/) includes information about [subscriptions](https://www.gitlab.com/subscription/), [consultancy](https://www.gitlab.com/consultancy/), the [community](https://www.gitlab.com/community/) and the [hosted GitLab Cloud](https://www.gitlab.com/cloud/).
* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/)
* [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ce/) offers additional features that are useful for larger organizations (100+ users).
* [GitLab Enterprise Edition](https://www.gitlab.com/features/) offers additional features that are useful for larger organizations (100+ users).
* [GitLab CI](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab.
* [GitLab CI](https://www.gitlab.com/gitlab-ci/) is a continuous integration (CI) server that is easy to integrate with GitLab.
* Unofficial third-party [iPhone app](http://gitlabcontrol.com/) and [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) for GitLab
......@@ -73,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
......@@ -98,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
......@@ -142,7 +135,7 @@ or start each component separately
* [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.
* [Feature request 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.
......@@ -155,12 +148,3 @@ or start each component separately
* [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.
### Getting in touch
* [Core team](http://gitlab.org/team/)
* [Contributors](http://contributors.gitlab.org/)
* [Community](http://gitlab.org/community/)
// 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
......@@ -62,11 +88,6 @@ $ ->
$('.remove-row').bind 'ajax:success', ->
$(this).closest('li').fadeOut()
# Click a .appear-link, appear-data fadeout
$(".appear-link").on 'click', (e) ->
$('.appear-data').fadeIn()
e.preventDefault()
# Initialize select2 selects
$('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)
......@@ -121,10 +142,6 @@ $ ->
$(@).next('table').show()
$(@).remove()
$(".diff-content").on "click", ".js-details-expand", ->
$(@).next('.js-details-contain').removeClass("hide")
$(@).remove()
(($) ->
# Disable an element and add the 'disabled' Bootstrap class
$.fn.extend disable: ->
......
$ ->
$("body").on "click", ".js-details-target", ->
container = $(@).closest(".js-details-container")
container.toggleClass("open")
# Show details content. Hides link after click.
#
# %div
# %a.js-details-expand
# %div.js-details-content
#
$("body").on "click", ".js-details-expand", (e) ->
$(@).next('.js-details-content').removeClass("hide")
$(@).hide()
e.preventDefault()
$ ->
$("body").on "click", ".js-toggler-target", ->
container = $(@).closest(".js-toggler-container")
container.toggleClass("on")
$("body").on "click", ".js-toggle-visibility-link", (e) ->
# Toggle button. Show/hide content inside parent container.
# Button does not change visibility. If button has icon - it changes chevron style.
#
# %div.js-toggle-container
# %a.js-toggle-button
# %div.js-toggle-content
#
$("body").on "click", ".js-toggle-button", (e) ->
$(@).find('i').
toggleClass('icon-chevron-down').
toggleClass('icon-chevron-up')
container = $(".js-toggle-visibility-container")
container.toggleClass("hide")
e.preventDefault()
$("body").on "click", ".js-toggle-button", (e) ->
$(@).closest(".js-toggle-container").find(".js-toggle-content").toggle()
e.preventDefault()
......@@ -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";
......
......@@ -298,10 +298,6 @@ img.emoji {
width: 20px;
}
.appear-data {
display: none;
}
.chart {
overflow: hidden;
height: 220px;
......@@ -359,3 +355,7 @@ table {
@media (max-width: $screen-xs-max) {
.container .content { margin-top: 20px; }
}
.wiki .highlight, .note-body .highlight {
margin-bottom: 9px;
}
/* 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;
......
......@@ -42,7 +42,7 @@
}
}
padding: 14px 0px;
padding: 12px 0px;
border-bottom: 1px solid #eee;
.event-title {
color: #333;
......
......@@ -273,3 +273,9 @@ header {
}
}
}
@media (max-width: $screen-xs-max) {
#nprogress .spinner {
right: 35px !important;
}
}
......@@ -189,7 +189,6 @@ ul.notes {
}
/**
* Line note button on the side of diffs
*/
......
class Admin::BackgroundJobsController < Admin::ApplicationController
def show
ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o euser,pid,pcpu,pmem,stat,start,command))
ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o pid,pcpu,pmem,stat,start,command))
@sidekiq_processes = ps_output.split("\n").grep(/sidekiq/)
end
end
......@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
before_filter :check_password_expiration
around_filter :set_current_user_for_thread
before_filter :add_abilities
before_filter :ldap_security_check
before_filter :dev_tools if Rails.env == 'development'
before_filter :default_headers
before_filter :add_gon_variables
......@@ -179,11 +180,30 @@ class ApplicationController < ActionController::Base
end
end
def ldap_security_check
if current_user && current_user.requires_ldap_check?
gitlab_ldap_access do |access|
if access.allowed?(current_user)
current_user.last_credential_check_at = Time.now
current_user.save
else
sign_out current_user
flash[:alert] = "Access denied for your LDAP account."
redirect_to new_user_session_path
end
end
end
end
def event_filter
filters = cookies['event_filter'].split(',') if cookies['event_filter'].present?
@event_filter ||= EventFilter.new(filters)
end
def gitlab_ldap_access(&block)
Gitlab::LDAP::Access.open { |access| block.call(access) }
end
# JSON for infinite scroll via Pager object
def pager_json(partial, count)
html = render_to_string(
......
......@@ -22,6 +22,8 @@ class DashboardController < ApplicationController
@last_push = current_user.recent_push
@publicish_project_count = Project.publicish(current_user).count
respond_to do |format|
format.html
format.json { pager_json("events/_events", @events.count) }
......
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
......
......@@ -5,7 +5,7 @@ class ProjectsController < ApplicationController
# Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'navless', only: [:new, :create, :fork]
......@@ -21,16 +21,9 @@ class ProjectsController < ApplicationController
def create
@project = ::Projects::CreateService.new(current_user, params[:project]).execute
flash[:notice] = 'Project was successfully created.' if @project.saved?
respond_to do |format|
flash[:notice] = 'Project was successfully created.' if @project.saved?
format.html do
if @project.saved?
redirect_to @project
else
render "new"
end
end
format.js
end
end
......@@ -55,6 +48,11 @@ class ProjectsController < ApplicationController
end
def show
if @project.import_in_progress?
redirect_to import_project_path(@project)
return
end
return authenticate_user! unless @project.public? || current_user
limit = (params[:limit] || 20).to_i
......@@ -67,9 +65,7 @@ class ProjectsController < ApplicationController
if @project.empty_repo?
render "projects/empty", layout: user_layout
else
if current_user
@last_push = current_user.recent_push(@project.id)
end
@last_push = current_user.recent_push(@project.id) if current_user
render :show, layout: user_layout
end
end
......@@ -77,6 +73,28 @@ class ProjectsController < ApplicationController
end
end
def import
if project.import_finished?
redirect_to @project
return
end
end
def retry_import
unless @project.import_failed?
redirect_to import_project_path(@project)
end
@project.import_url = params[:project][:import_url]
if @project.save
@project.reload
@project.import_retry
end
redirect_to import_project_path(@project)
end
def destroy
return access_denied! unless can?(current_user, :remove_project, project)
......@@ -106,7 +124,7 @@ class ProjectsController < ApplicationController
def autocomplete_sources
@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 } }
......
......@@ -7,6 +7,7 @@ class SearchController < ApplicationController
if @project
return access_denied! unless can?(current_user, :download_code, @project)
@search_results = Search::ProjectService.new(@project, current_user, params).execute
else
@search_results = Search::GlobalService.new(current_user, params).execute
......
......@@ -19,6 +19,9 @@ class SnippetsController < ApplicationController
def user_index
@user = User.find_by(username: params[:username])
render_404 and return unless @user
@snippets = @user.snippets.fresh.non_expired
if @user == current_user
......
......@@ -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
......
......@@ -146,8 +146,7 @@ module ApplicationHelper
def authbutton(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag("authbuttons/#{file_name}",
alt: "Sign in with #{provider.to_s.titleize}")
image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}")
end
def simple_sanitize(str)
......
......@@ -35,7 +35,6 @@ module GitlabMarkdownHelper
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
filter_html: true,
with_toc_data: true,
hard_wrap: true,
safe_links_only: true
}.merge(options))
@markdown = Redcarpet::Markdown.new(gitlab_renderer,
......@@ -45,7 +44,7 @@ module GitlabMarkdownHelper
fenced_code_blocks: true,
autolink: true,
strikethrough: true,
lax_html_blocks: true,
lax_spacing: true,
space_after_headers: true,
superscript: true)
end
......@@ -64,8 +63,7 @@ module GitlabMarkdownHelper
# project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq
# ref - name of the branch or reference, eg. stable
# requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from
# wiki - whether the markdown is from wiki or not
def create_relative_links(text, project, ref, requested_path, wiki = false)
def create_relative_links(text, project, ref, requested_path)
@path_to_satellite = project.satellite.path
project_path_with_namespace = project.path_with_namespace
paths = extract_paths(text)
......@@ -135,12 +133,12 @@ module GitlabMarkdownHelper
end
# Checks if the path exists in the repo
# eg. checks if doc/README.md exists, if it doesn't then it is a wiki link
# eg. checks if doc/README.md exists, if not then link to blob
def path_with_ref(path, ref)
if file_exists?(path)
"#{local_path(path)}/#{correct_ref(ref)}"
else
"wikis"
"blob/#{correct_ref(ref)}"
end
end
......
......@@ -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
......
......@@ -10,7 +10,7 @@ module ProfileHelper
end
def show_profile_social_tab?
Gitlab.config.omniauth.enabled && !current_user.ldap_user?
enabled_social_providers.any? && !current_user.ldap_user?
end
def show_profile_remove_tab?
......
......@@ -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)
......
......@@ -3,7 +3,7 @@ module Emails
def group_access_granted_email(user_group_id)
@membership = UsersGroup.find(user_group_id)
@group = @membership.group
@target_url = group_url(@group)
mail(to: @membership.user.email,
subject: subject("Access to group was granted"))
end
......
......@@ -3,6 +3,7 @@ module Emails
def new_issue_email(recipient_id, issue_id)
@issue = Issue.find(issue_id)
@project = @issue.project
@target_url = project_issue_url(@project, @issue)
mail(from: sender(@issue.author_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
......@@ -12,6 +13,7 @@ module Emails
@issue = Issue.find(issue_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @issue.project
@target_url = project_issue_url(@project, @issue)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
......@@ -21,6 +23,7 @@ module Emails
@issue = Issue.find issue_id
@project = @issue.project
@updated_by = User.find updated_by_user_id
@target_url = project_issue_url(@project, @issue)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
......@@ -31,6 +34,7 @@ module Emails
@issue_status = status
@project = @issue.project
@updated_by = User.find updated_by_user_id
@target_url = project_issue_url(@project, @issue)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
......
......@@ -3,6 +3,7 @@ module Emails
def new_merge_request_email(recipient_id, merge_request_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),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
......@@ -12,6 +13,7 @@ module Emails
@merge_request = MergeRequest.find(merge_request_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
......@@ -21,15 +23,17 @@ module Emails
@merge_request = MergeRequest.find(merge_request_id)
@updated_by = User.find updated_by_user_id
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
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
mail(from: sender(@merge_request.author_id_of_changes),
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
end
......
......@@ -4,6 +4,7 @@ module Emails
@note = Note.find(note_id)
@commit = @note.noteable
@project = @note.project
@target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@commit.title} (#{@commit.short_id})"))
......@@ -13,6 +14,7 @@ module Emails
@note = Note.find(note_id)
@issue = @note.noteable
@project = @note.project
@target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
......@@ -22,6 +24,7 @@ module Emails
@note = Note.find(note_id)
@merge_request = @note.noteable
@project = @note.project
@target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
......@@ -30,6 +33,7 @@ module Emails
def note_wall_email(recipient_id, note_id)
@note = Note.find(note_id)
@project = @note.project
@target_url = project_wall_url(@note.project, anchor: "note_#{@note.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("Note on wall"))
......
......@@ -3,6 +3,7 @@ module Emails
def new_user_email(user_id, password)
@user = User.find(user_id)
@password = password
@target_url = user_url(@user)
mail(to: @user.email, subject: subject("Account was created for you"))
end
......@@ -15,6 +16,7 @@ module Emails
def new_ssh_key_email(key_id)
@key = Key.find(key_id)
@user = @key.user
@target_url = user_url(@user)
mail(to: @user.email, subject: subject("SSH key was added to your account"))
end
end
......
......@@ -3,6 +3,7 @@ module Emails
def project_access_granted_email(user_project_id)
@users_project = UsersProject.find user_project_id
@project = @users_project.project
@target_url = project_url(@project)
mail(to: @users_project.user.email,
subject: subject("Access to project was granted"))
end
......@@ -10,6 +11,7 @@ module Emails
def project_was_moved_email(project_id, user_id)
@user = User.find user_id
@project = Project.find project_id
@target_url = project_url(@project)
mail(to: @user.email,
subject: subject("Project was moved"))
end
......@@ -21,6 +23,11 @@ module Emails
@commits = Commit.decorate(compare.commits)
@diffs = compare.diffs
@branch = branch
if @commits.length > 1
@target_url = project_compare_url(@project, from: @commits.first, to: @commits.last)
else
@target_url = project_commit_url(@project, @compare.commit)
end
mail(from: sender(author_id),
to: recipient,
......
......@@ -240,6 +240,7 @@ class Ability
can_manage = group_abilities(user, group).include?(:manage_group)
if can_manage && (user != target_user)
rules << :modify
rules << :destroy
end
if !group.last_owner?(user) && (can_manage || (user == target_user))
rules << :destroy
......
......@@ -133,7 +133,7 @@ class MergeRequest < ActiveRecord::Base
end
def reload_code
if merge_request_diff && opened?
if merge_request_diff && open?
merge_request_diff.reload_content
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
......
......@@ -28,7 +28,6 @@ class Project < ActiveRecord::Base
include Gitlab::VisibilityLevel
extend Enumerize
default_value_for :imported, false
default_value_for :archived, false
ActsAsTaggableOn.strict_case_match = true
......@@ -57,15 +56,13 @@ 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
has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
# Merge requests from source project should be kept when source project was removed
has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy
has_many :services, dependent: :destroy
has_many :events, dependent: :destroy
......@@ -74,10 +71,8 @@ class Project < ActiveRecord::Base
has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
has_many :protected_branches, dependent: :destroy
has_many :users_projects, dependent: :destroy
has_many :users, through: :users_projects
has_many :deploy_keys_projects, dependent: :destroy
has_many :deploy_keys, through: :deploy_keys_projects
......@@ -97,15 +92,12 @@ class Project < ActiveRecord::Base
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
validates :import_url,
format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" },
if: :import?
validate :check_limit, on: :create
# Scopes
......@@ -118,14 +110,36 @@ class Project < ActiveRecord::Base
scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
scope :non_archived, -> { where(archived: false) }
enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
state_machine :import_status, initial: :none do
event :import_start do
transition :none => :started
end
event :import_finish do
transition :started => :finished
end
event :import_fail do
transition :started => :failed
end
event :import_retry do
transition :failed => :started
end
state :started
state :finished
state :failed
after_transition any => :started, :do => :add_import_job
end
class << self
def public_and_internal_levels
[Project::PUBLIC, Project::INTERNAL]
......@@ -164,15 +178,13 @@ class Project < ActiveRecord::Base
end
def find_with_namespace(id)
if id.include?("/")
return nil unless id.include?("/")
id = id.split("/")
namespace = Namespace.find_by(path: id.first)
return nil unless namespace
where(namespace_id: namespace.id).find_by(path: id.second)
else
where(path: id, namespace_id: nil).last
end
end
def visibility_levels
......@@ -202,12 +214,28 @@ class Project < ActiveRecord::Base
id && persisted?
end
def add_import_job
RepositoryImportWorker.perform_in(2.seconds, id)
end
def import?
import_url.present?
end
def imported?
imported
import_finished?
end
def import_in_progress?
import? && import_status == 'started'
end
def import_failed?
import_status == 'failed'
end
def import_finished?
import_status == 'finished'
end
def check_limit
......@@ -277,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?
......@@ -361,18 +389,17 @@ class Project < ActiveRecord::Base
branch_name = ref.gsub("refs/heads/", "")
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
# Close merge requests
mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a
mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) }
# Update code for merge requests into project between project branches
mrs = self.merge_requests.opened.by_branch(branch_name).to_a
# Update code for merge requests between project and project fork
mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests
mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a
mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) }
true
end
......
......@@ -17,9 +17,10 @@
class ProjectHook < WebHook
belongs_to :project
attr_accessible :push_events, :issues_events, :merge_requests_events
attr_accessible :push_events, :issues_events, :merge_requests_events, :tag_push_events
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
scope :issue_hooks, -> { where(issues_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) }
end
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.ping(message.compose)
end
private
def project_name
project.name_with_namespace.gsub(/\s/, '')
end
def project_url
project.web_url
end
end
......@@ -20,6 +20,8 @@ class Snippet < ActiveRecord::Base
attr_accessible :title, :content, :file_name, :expires_at, :private
default_value_for :private, true
belongs_to :author, class_name: "User"
has_many :notes, as: :noteable, dependent: :destroy
......
......@@ -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
......@@ -275,7 +275,9 @@ class User < ActiveRecord::Base
# Projects user has access to
def authorized_projects
@authorized_projects ||= begin
project_ids = (personal_projects.pluck(:id) + groups_projects.pluck(:id) + projects.pluck(:id)).uniq
project_ids = personal_projects.pluck(:id)
project_ids += groups_projects.pluck(:id)
project_ids += projects.pluck(:id).uniq
Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC')
end
end
......@@ -406,6 +408,14 @@ class User < ActiveRecord::Base
end
end
def requires_ldap_check?
if ldap_user?
!last_credential_check_at || (last_credential_check_at + 1.hour) < Time.now
else
false
end
end
def solo_owned_groups
@solo_owned_groups ||= owned_groups.select do |group|
group.owners == [self]
......
class ProjectObserver < BaseObserver
def after_create(project)
project.update_column(:last_activity_at, project.created_at)
return true if project.forked?
if project.import?
RepositoryImportWorker.perform_in(5.seconds, project.id)
else
GitlabShellWorker.perform_async(
:add_repository,
project.path_with_namespace
)
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
end
if project.wiki_enabled?
begin
# force the creation of a wiki,
GollumWiki.new(project, project.owner).wiki
rescue GollumWiki::CouldNotCreateWikiError => ex
# Prevent project observer crash
# if failed to create wiki
nil
end
end
end
def after_update(project)
project.send_move_instructions if project.namespace_id_changed?
project.rename_repo if project.path_changed?
......
class GitTagPushService
attr_accessor :project, :user, :push_data
def execute(project, user, oldrev, newrev, ref)
@project, @user = project, user
@push_data = create_push_data(oldrev, newrev, ref)
create_push_event
project.repository.expire_cache
project.execute_hooks(@push_data.dup, :tag_push_hooks)
end
private
def create_push_data(oldrev, newrev, ref)
data = {
ref: ref,
before: oldrev,
after: newrev,
user_id: user.id,
user_name: user.name,
project_id: project.id,
repository: {
name: project.name,
url: project.url_to_repo,
description: project.description,
homepage: project.web_url
}
}
end
def create_push_event
Event.create!(
project: project,
action: Event::PUSHED,
data: push_data,
author_id: push_data[:user_id]
)
end
end
......@@ -12,7 +12,7 @@ module MergeRequests
merge_request.author_id_of_changes = current_user.id
merge_request.merge
notification.merge_mr(merge_request)
notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request)
execute_project_hooks(merge_request)
......
......@@ -10,7 +10,7 @@ module MergeRequests
merge_request.author_id_of_changes = current_user.id
merge_request.merge
notification.merge_mr(merge_request)
notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request)
execute_project_hooks(merge_request)
......
......@@ -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 }
......
......@@ -58,6 +58,29 @@ module Projects
user: current_user
)
end
@project.update_column(:last_activity_at, @project.created_at)
if @project.import?
@project.import_start
else
GitlabShellWorker.perform_async(
:add_repository,
@project.path_with_namespace
)
end
if @project.wiki_enabled?
begin
# force the creation of a wiki,
GollumWiki.new(@project, @project.owner).wiki
rescue GollumWiki::CouldNotCreateWikiError => ex
# Prevent project observer crash
# if failed to create wiki
nil
end
end
end
@project
......
......@@ -14,10 +14,9 @@ module Search
group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
projects = Project.accessible_to(current_user)
projects = projects.where(namespace_id: group.id) if group
projects = projects.search(query)
project_ids = projects.pluck(:id)
result[:projects] = projects.limit(20)
result[:projects] = projects.search(query).limit(20)
result[:merge_requests] = MergeRequest.in_projects(project_ids).search(query).order('updated_at DESC').limit(20)
result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20)
result[:total_results] = %w(projects issues merge_requests).sum { |items| result[items.to_sym].size }
......
......@@ -14,27 +14,21 @@
%table.table
%thead
%th USER
%th
%th PID
%th
%th CPU
%th
%th MEM
%th
%th STATE
%th
%th START
%th
%th COMMAND
%th
- @sidekiq_processes.split("\n").each do |process|
%tbody
- @sidekiq_processes.each do |process|
- next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
- data = process.gsub!(/\s+/m, '|').strip.split('|')
- data = process.strip.split(' ')
%tr
- 6.times do
%td= Settings.gitlab.user
- 5.times do
%td= data.shift
%td
%td= data.join(" ")
%td= data.join(' ')
.clearfix
%p
......
......@@ -34,3 +34,18 @@
= link_to new_group_path, class: "btn btn-new" do
New group »
-if @publicish_project_count > 0
%hr
%div
.dashboard-intro-icon
%i.icon-globe
%div
%p.slead
There are
%strong= @publicish_project_count
public projects on this server.
%br
Public projects are an easy way to allow everyone to have read-only access.
.link_holder
= link_to public_projects_path, class: "btn btn-new" do
Browse public projects »
......@@ -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
......@@ -9,7 +9,7 @@
%hr
.clearfix
.clearfix.js-toggle-container
= form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find member by name', class: 'form-control search-text-input input-mn-300' }
......@@ -17,11 +17,11 @@
- if current_user && current_user.can?(:manage_group, @group)
.pull-right
= link_to '#', class: 'btn btn-new js-toggle-visibility-link' do
= link_to '#', class: 'btn btn-new js-toggle-button' do
Add members
%i.icon-chevron-down
.js-toggle-visibility-container.hide.new-group-member-holder
.js-toggle-content.hide.new-group-member-holder
= render "new_group_member"
.ui-box.prepend-top-20
......
......@@ -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.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'
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li
%a
%div.hide.turbolink-spinner
%i.icon-refresh.icon-spin
%li.visible-xs
= link_to "Sign in", new_session_path(:user)
......@@ -3,20 +3,24 @@
%meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
%title
GitLab
:css
p.details {
font-style:italic;
color:#777
}
.footer p {
font-size:small;
color:#777
}
%body
%h1{style: "background: #EEE; border-bottom: 1px solid #DDD; color: #474D57; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"}
GitLab
- if @project
\|
= link_to @project.name_with_namespace, project_url(@project), style: 'color: #29B; text-decoration: none'
%table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"}
%tr
%td{align: "left", style: "margin: 0; padding: 10px;"}
%div.content
= yield
%div.footer{style: "margin-top: 10px;"}
%p
\—
%br
%tr
%td{align: "left", style: "margin: 0; padding: 10px;"}
%p{style: "font-size:small;color:#777"}
- if @project
You're receiving this notification because you are a member of the #{@project.name_with_namespace} project team.
You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team.
- if @target_url
#{link_to "View in GitLab", @target_url}
......@@ -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
%p
%strong #{@note.author_name}
wrote:
%cite{style: 'color: #666'}
%div
= markdown(@note.note)
%p
= "Issue was closed by #{@updated_by.name}"
%p
= "Issue ##{@issue.iid}"
= link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title
%p
= "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}"
%p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
!= merge_path_description(@merge_request, '&rarr;')
%p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
= "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}"
%p
= "You have been granted #{@membership.human_access} access to group"
%p
= link_to group_url(@group) do
= @group.name
%p
= "Issue was #{@issue_status} by #{@updated_by.name}"
%p
= "Issue ##{@issue.iid}"
= link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title
%p
= "Merge Request #{@merge_request.iid} was merged"
%p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
!= merge_path_description(@merge_request, '&rarr;')
%p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
= "Merge Request !#{@merge_request.iid} was merged"
%p
New Issue was created.
%p
= "Issue ##{@issue.iid}"
= link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title
%p
Author: #{@issue.author_name}
%p
-if @issue.description
= markdown(@issue.description)
- if @issue.assignee_id.present?
%p
Assignee: #{@issue.assignee_name}
%p
= "New Merge Request ##{@merge_request.iid}"
%p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
%p.details
!= merge_path_description(@merge_request, '&rarr;')
%p
- if @merge_request.assignee_id.present?
%p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
-if @merge_request.description
= markdown(@merge_request.description)
%p
= "New comment for Commit #{@commit.short_id}"
= link_to_gfm truncate(@commit.title, length: 16), project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")
= render 'note_message'
%p
= "New comment for Issue ##{@issue.iid}"
= link_to_gfm truncate(@issue.title, length: 35), project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")
= render 'note_message'
%p
- if @note.for_diff_line?
= link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")
- else
= link_to "New comment", project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")
for Merge Request ##{@merge_request.iid}
%cite "#{truncate(@merge_request.title, length: 20)}"
- if @note.diff_file_name
%p.details
New comment on diff for
= link_to @note.diff_file_name, @target_url
\:
= render 'note_message'
%p
New message on
= link_to "Project Wall", project_wall_url(@note.project, anchor: "note_#{@note.id}")
= render 'note_message'
%p
= "Reassigned Issue ##{@issue.iid}"
= link_to_gfm truncate(@issue.title, length: 30), project_issue_url(@issue.project, @issue)
%p
Assignee changed
- if @previous_assignee
......
%p
= "Reassigned Merge Request ##{@merge_request.iid}"
= link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
Assignee changed
- if @previous_assignee
......
.pull-right
%div
- if @notes_count > 0
%span.btn.disabled.grouped
%span.btn.disabled.btn-grouped
%i.icon-comment
= @notes_count
.pull-left.btn-group
......@@ -47,7 +47,7 @@
- if @branches.any?
and in
= link_to("#{pluralize(@branches.count, "other branch")}", "#", class: "js-details-expand")
%span.js-details-contain.hide
%span.js-details-content.hide
= commit_branches_links(@project, @branches)
.commit-box
......
......@@ -44,7 +44,7 @@
- file = project.repository.blob_at(@commit.id, diff.new_path)
- file = project.repository.blob_at(@commit.parent_id, diff.old_path) unless file
- next unless file
.diff-file{id: "diff-#{i}"}
.diff-file.js-toggle-container{id: "diff-#{i}"}
.diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"}
- if diff.deleted_file
%span= diff.old_path
......@@ -60,6 +60,11 @@
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
.diff-btn-group
= link_to "#", class: "js-toggle-button btn btn-small" do
%i.icon-chevron-down
Diff comments
&nbsp;
- if @merge_request && @merge_request.source_project
= link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do
Edit
......
- if @project.saved?
- if @project.import?
:plain
location.href = "#{import_project_path(@project)}";
- else
:plain
location.href = "#{project_path(@project)}";
- else
......
......@@ -93,17 +93,18 @@
.danger-settings.js-toggle-container
.centered-light-block
%h3
%i.icon-warning-sign
Dangerous settings
%p Project settings below may result in data loss!
= link_to '#', class: 'btn js-toggle-visibility-link' do
= link_to '#', class: 'btn js-toggle-button' do
Show it to me
%i.icon-chevron-down
.js-toggle-visibility-container.hide
.js-toggle-content.hide
- if can? current_user, :archive_project, @project
.ui-box.ui-box-danger
.title
......
= render "home_panel"
- if @project.import? && !@project.imported
.save-project-loader
%center
%h2
%i.icon-spinner.icon-spin
Importing repository.
%p.monospace git clone --bare #{@project.import_url}
%p Please wait while we import the repository for you. Refresh at will.
:javascript
new ProjectImport();
- else
%div.git-empty
%div.git-empty
%fieldset
%legend Git global setup:
%pre.dark
......@@ -41,6 +29,6 @@
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
git push -u origin master
- if can? current_user, :remove_project, @project
- if can? current_user, :remove_project, @project
.prepend-top-20
= link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
......@@ -26,6 +26,13 @@
%strong Push events
%p.light
This url will be triggered by a push to the repository
%div
= f.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= f.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
%div
= f.check_box :issues_events, class: 'pull-left'
.prepend-left-20
......@@ -56,6 +63,6 @@
.clearfix
%span.monospace= hook.url
%p
- %w(push_events issues_events merge_requests_events).each do |trigger|
- %w(push_events tag_push_events issues_events merge_requests_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
- if @project.import_in_progress?
.save-project-loader
%center
%h2
%i.icon-spinner.icon-spin
Import in progress.
%p.monospace git clone --bare #{@project.import_url}
%p Please wait while we import the repository for you. Refresh at will.
:javascript
new ProjectImport();
- elsif @project.import_failed?
.save-project-loader
%center
%h2
Import failed. Retry?
%hr
- if can?(current_user, :admin_project, @project)
= form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f|
.form-group.import-url-data
= f.label :import_url, class: 'control-label' do
%span Import existing repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.bs-callout.bs-callout-info
This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 2 minutes. For big repositories, use a clone/push combination.
.form-actions
= f.submit 'Retry import', class: "btn btn-create", tabindex: 4
- 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|
.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
%ul
- @merge_request.errors.full_messages.each do |msg|
%li= 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();
});
......@@ -3,5 +3,5 @@
var mrTitle = $('#merge_request_title');
if(mrTitle.val().length == 0) {
mrTitle.val("#{params[:ref].titleize}");
mrTitle.val("#{params[:ref].humanize}");
}
- unless @allowed_to_merge
- if @project.archived?
.bs-callout.bs-callout-warning
%strong Archived projects cannot be committed to!
- else
.bs-callout
%strong You don't have permission to merge this MR
......@@ -15,12 +19,13 @@
= link_to "click here", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
for instructions.
%br
.js-toggle-container
%p
If you want to modify merge commit message -
%strong
= link_to "click here", "#", class: "modify-merge-commit-link js-toggle-visibility-link", title: "Modify merge commit message"
.js-toggle-visibility-container.hide
= link_to "click here", "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message"
.js-toggle-content.hide
.form-group
= label_tag :merge_commit_message, "Commit message", class: 'control-label'
.col-sm-10
......
......@@ -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
......
......@@ -9,18 +9,6 @@
%strong Project name
.col-sm-10
= f.text_field :name, placeholder: "Example Project", class: "form-control", tabindex: 1, autofocus: true
.help-inline
= link_to "#", class: 'js-toggle-visibility-link' do
%span Customize repository name?
.form-group.js-toggle-visibility-container.hide
= f.label :path, class: 'control-label' do
%span Repository name
.col-sm-10
.input-group
= f.text_field :path, class: 'form-control'
%span.input-group-addon .git
- if current_user.can_select_namespace?
.form-group
......@@ -29,19 +17,42 @@
.col-sm-10
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2}
%hr
.js-toggle-container
.form-group
.col-sm-2
.col-sm-10
= link_to "#", class: 'appear-link' do
= link_to "#", class: 'js-toggle-button' do
%i.icon-edit
%span Customize repository name?
.js-toggle-content.hide
.form-group
= f.label :path, class: 'control-label' do
%span Repository name
.col-sm-10
.input-group
= f.text_field :path, class: 'form-control'
%span.input-group-addon .git
.js-toggle-container
.form-group
.col-sm-2
.col-sm-10
= link_to "#", class: 'js-toggle-button' do
%i.icon-upload-alt
%span Import existing repository?
.form-group.appear-data.import-url-data
.js-toggle-content.hide
.form-group.import-url-data
= f.label :import_url, class: 'control-label' do
%span Import existing repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.light
URL must be cloneable
.bs-callout.bs-callout-info
This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 2 minutes. For big repositories, use a clone/push combination.
%hr
.form-group
= f.label :description, class: 'control-label' do
Description
......
- note = notes.first # example note
-# Check if line want not changed since comment was left
- if !defined?(line) || line == note.diff_line
%tr.notes_holder
%tr.notes_holder.js-toggle-content
%td.notes_line{ colspan: 2 }
%span.btn.disabled
%i.icon-comment
......
- note1 = notes1.first # example note
- note2 = notes2.first # example note
%tr.notes_holder
%tr.notes_holder.js-toggle-content
-# Check if line want not changed since comment was left
/- if !defined?(line1) || line1 == note1.diff_line
- if note1
......
......@@ -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
......
......@@ -29,10 +29,20 @@ class PostReceive
return false
end
if tag?(ref)
GitTagPushService.new.execute(project, user, oldrev, newrev, ref)
else
GitPushService.new.execute(project, user, oldrev, newrev, ref)
end
end
def log(message)
Gitlab::GitLogger.error("POST-RECEIVE: #{message}")
end
private
def tag?(ref)
!!(/refs\/tags\/(.*)/.match(ref))
end
end
......@@ -11,11 +11,11 @@ class RepositoryImportWorker
project.import_url)
if result
project.imported = true
project.import_finish
project.save
project.satellite.create unless project.satellite.exists?
else
project.imported = false
project.import_fail
end
end
end
......@@ -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 ]
......
......@@ -121,7 +121,6 @@ production: &base
ldap:
enabled: false
host: '_your_ldap_server'
base: '_the_base_where_you_search_for_users'
port: 636
uid: 'sAMAccountName'
method: 'ssl' # "tls" or "ssl" or "plain"
......@@ -138,6 +137,20 @@ production: &base
# disable this setting, because the userPrincipalName contains an '@'.
allow_username_or_email_login: true
# Base where we can search for users
#
# Ex. ou=People,dc=gitlab,dc=example
#
base: ''
# Filter LDAP users
#
# Format: RFC 4515
# Ex. (employeeType=developer)
#
user_filter: ''
## OmniAuth settings
omniauth:
# Allow login via Twitter, Google, etc. using OmniAuth providers
......
# 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
......@@ -179,6 +179,8 @@ Gitlab::Application.routes.draw do
post :archive
post :unarchive
get :autocomplete_sources
get :import
put :retry_import
end
scope module: :projects do
......
worker_processes 2
timeout 30
......@@ -8,8 +8,7 @@ User.seed(:id, [
password_confirmation: "5iveL!fe",
admin: true,
projects_limit: 100,
theme_id: Gitlab::Theme::MARS
theme_id: Gitlab::Theme::MARS,
confirmed_at: DateTime.now
}
])
User.find(1).confirm!
project_urls = [
Gitlab::Seeder.quiet do
project_urls = [
'https://github.com/documentcloud/underscore.git',
'https://github.com/diaspora/diaspora.git',
'https://github.com/diaspora/diaspora-project-site.git',
......@@ -15,9 +16,9 @@ project_urls = [
'https://github.com/twitter/typeahead.js.git',
'https://github.com/h5bp/html5-boilerplate.git',
'https://github.com/h5bp/mobile-boilerplate.git',
]
]
project_urls.each_with_index do |url, i|
project_urls.each_with_index do |url, i|
group_path, project_path = url.split('/')[-2..-1]
group = Group.find_by(path: group_path)
......@@ -50,4 +51,5 @@ project_urls.each_with_index do |url, i|
puts project.errors.full_messages
print 'F'
end
end
end
......@@ -6,6 +6,7 @@ Gitlab::Seeder.quiet do
username: Faker::Internet.user_name,
name: Faker::Name.name,
email: Faker::Internet.email,
confirmed_at: DateTime.now
}])
print '.'
rescue ActiveRecord::RecordNotSaved
......
ActiveRecord::Base.observers.disable :all
Gitlab::Seeder.quiet do
Group.all.each do |group|
User.all.sample(4).each do |user|
......
ActiveRecord::Base.observers.disable :all
Gitlab::Seeder.quiet do
(1..300).each do |i|
# Random Project
......@@ -12,9 +10,7 @@ Gitlab::Seeder.quiet do
user_id = user.id
begin
Thread.current[:current_user] = user
Gitlab::Seeder.by_user(user) do
Issue.seed(:id, [{
id: i,
project_id: project.id,
......@@ -25,8 +21,6 @@ Gitlab::Seeder.quiet do
title: Faker::Lorem.sentence(6),
description: Faker::Lorem.sentence
}])
ensure
Thread.current[:current_user] = nil
end
print('.')
end
......
ActiveRecord::Base.observers.disable :all
Gitlab::Seeder.quiet do
(1..100).each do |i|
# Random Project
......@@ -17,9 +15,8 @@ Gitlab::Seeder.quiet do
next if branches.uniq.size < 2
user_id = user.id
begin
Thread.current[:current_user] = user
Gitlab::Seeder.by_user(user) do
MergeRequest.seed(:id, [{
id: i,
source_branch: branches.first,
......@@ -31,8 +28,6 @@ Gitlab::Seeder.quiet do
milestone: project.milestones.sample,
title: Faker::Lorem.sentence(6)
}])
ensure
Thread.current[:current_user] = nil
end
print('.')
end
......
ActiveRecord::Base.observers.enable :all
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,
......
ActiveRecord::Base.observers.disable :all
Gitlab::Seeder.quiet do
contents = [
`curl https://gist.github.com/randx/4275756/raw/da2f262920c96d1a970d48bf2e99147954b1f4bd/glus1204.sh`,
......
Gitlab::Seeder.quiet do
Issue.all.limit(10).each_with_index do |issue, i|
5.times do
user = issue.project.team.users.sample
Gitlab::Seeder.by_user(user) do
Note.seed(:id, [{
project_id: issue.project.id,
author_id: user.id,
note: Faker::Lorem.sentence,
noteable_id: issue.id,
noteable_type: 'Issue'
}])
print '.'
end
end
end
end
class AddPermissionCheckToUser < ActiveRecord::Migration
def change
add_column :users, :last_credential_check_at, :datetime
end
end
class AddTagPushHooksToProjectHook < ActiveRecord::Migration
def change
add_column :web_hooks, :tag_push_events, :boolean, default: false
end
end
class AddImportStatusToProject < ActiveRecord::Migration
def change
add_column :projects, :import_status, :string
end
end
class MigrateAlreadyImportedProjects < ActiveRecord::Migration
def up
Project.where(imported: true).update_all(import_status: "finished")
Project.where(imported: false).update_all(import_status: "none")
remove_column :projects, :imported
end
def down
add_column :projects, :imported, :boolean, default: false
Project.where(import_status: 'finished').update_all(imported: true)
end
end
......@@ -11,15 +11,18 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20140304005354) do
ActiveRecord::Schema.define(version: 20140313092127) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "broadcast_messages", force: true do |t|
t.text "message", null: false
t.datetime "starts_at"
t.datetime "ends_at"
t.integer "alert_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "color"
t.string "font"
end
......@@ -27,8 +30,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
create_table "deploy_keys_projects", force: true do |t|
t.integer "deploy_key_id", null: false
t.integer "project_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
......@@ -49,8 +52,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.string "title"
t.text "data"
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "action"
t.integer "author_id"
end
......@@ -65,8 +68,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
create_table "forked_project_links", force: true do |t|
t.integer "forked_to_project_id", null: false
t.integer "forked_from_project_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
......@@ -76,8 +79,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.integer "assignee_id"
t.integer "author_id"
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "position", default: 0
t.string "branch_name"
t.text "description"
......@@ -95,8 +98,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
create_table "keys", force: true do |t|
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.text "key"
t.string "title"
t.string "type"
......@@ -107,8 +110,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
create_table "merge_request_diffs", force: true do |t|
t.string "state", default: "collected", null: false
t.text "st_commits", limit: 2147483647
t.text "st_diffs", limit: 2147483647
t.text "st_commits"
t.text "st_diffs"
t.integer "merge_request_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
......@@ -123,8 +126,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.integer "author_id"
t.integer "assignee_id"
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "milestone_id"
t.string "state"
t.string "merge_status"
......@@ -138,7 +141,7 @@ ActiveRecord::Schema.define(version: 20140304005354) do
add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_project_id", using: :btree
add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree
add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
......@@ -147,8 +150,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.integer "project_id", null: false
t.text "description"
t.date "due_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "state"
t.integer "iid"
end
......@@ -160,8 +163,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.string "name", null: false
t.string "path", null: false
t.integer "owner_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "type"
t.string "description", default: "", null: false
t.string "avatar"
......@@ -176,15 +179,15 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.text "note"
t.string "noteable_type"
t.integer "author_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "project_id"
t.string "attachment"
t.string "line_code"
t.string "commit_id"
t.integer "noteable_id"
t.text "st_diff"
t.boolean "system", default: false, null: false
t.text "st_diff"
end
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
......@@ -199,8 +202,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.string "name"
t.string "path"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "creator_id"
t.boolean "issues_enabled", default: true, null: false
t.boolean "wall_enabled", default: true, null: false
......@@ -211,21 +214,21 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.string "issues_tracker_id"
t.boolean "snippets_enabled", default: true, null: false
t.datetime "last_activity_at"
t.boolean "imported", default: false, null: false
t.string "import_url"
t.integer "visibility_level", default: 0, null: false
t.boolean "archived", default: false, null: false
t.string "import_status"
end
add_index "projects", ["creator_id"], name: "index_projects_on_owner_id", using: :btree
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
create_table "protected_branches", force: true do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
......@@ -235,8 +238,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.string "title"
t.string "token"
t.integer "project_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "active", default: false, null: false
t.string "project_url"
t.string "subdomain"
......@@ -249,11 +252,11 @@ ActiveRecord::Schema.define(version: 20140304005354) do
create_table "snippets", force: true do |t|
t.string "title"
t.text "content", limit: 2147483647
t.text "content"
t.integer "author_id", null: false
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "file_name"
t.datetime "expires_at"
t.boolean "private", default: true, null: false
......@@ -293,8 +296,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.boolean "admin", default: false, null: false
t.integer "projects_limit", default: 10
......@@ -316,6 +319,7 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.integer "notification_level", default: 1, null: false
t.datetime "password_expires_at"
t.integer "created_by_id"
t.datetime "last_credential_check_at"
t.string "avatar"
t.string "confirmation_token"
t.datetime "confirmed_at"
......@@ -338,8 +342,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
t.integer "group_access", null: false
t.integer "group_id", null: false
t.integer "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "notification_level", default: 3, null: false
end
......@@ -348,8 +352,8 @@ ActiveRecord::Schema.define(version: 20140304005354) do
create_table "users_projects", force: true do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "project_access", default: 0, null: false
t.integer "notification_level", default: 3, null: false
end
......@@ -361,13 +365,14 @@ ActiveRecord::Schema.define(version: 20140304005354) do
create_table "web_hooks", force: true do |t|
t.string "url"
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "type", default: "ProjectHook"
t.integer "service_id"
t.boolean "push_events", default: true, null: false
t.boolean "issues_events", default: false, null: false
t.boolean "merge_requests_events", default: false, null: false
t.boolean "tag_push_events", default: false
end
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
......
......@@ -148,6 +148,16 @@ Parameters:
"path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z"
}
"permissions": {
"project_access": {
"access_level": 10,
"notification_level": 3
},
"group_access": {
"access_level": 50,
"notification_level": 3
}
}
}
```
......@@ -611,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"
}
]
```
......@@ -3,7 +3,7 @@
# Software delivery
There are two editions of GitLab: [Enterprise Edition](https://www.gitlab.com/features/) (EE) and [Community Edition](http://gitlab.org/gitlab-ce/) (CE).
There are two editions of GitLab: [Enterprise Edition](https://www.gitlab.com/gitlab-ee/) (EE) and [Community Edition](https://www.gitlab.com/gitlab-ce/) (CE).
GitLab CE is delivered via git from the [gitlabhq repository](https://gitlab.com/gitlab-org/gitlab-ce/tree/master).
New versions of GitLab are released in stable branches and the master branch is for bleeding edge development.
......@@ -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).
......
# 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.
......
......@@ -4,7 +4,7 @@ this should be the highest numbered stable branch (example shown below).
![capture](http://i.imgur.com/d2AlIVj.png)
If this is unclear check the [GitLab Blog](http://blog.gitlab.org/) for installation guide links by version.
If this is unclear check the [GitLab Blog](https://www.gitlab.com/blog/) for installation guide links by version.
# Important notes
......@@ -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
......@@ -144,7 +144,7 @@ GitLab Shell is an ssh access and repository management software developed speci
# 5. Database
We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](doc/install/database_mysql.md).
We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md).
# Install the database packages
sudo apt-get install -y postgresql-9.1 postgresql-client libpq-dev
......@@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](do
## 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
......@@ -351,7 +351,7 @@ If all items are green, then congratulations on successfully installing GitLab!
Visit YOUR_SERVER in your web browser for your first GitLab login.
The setup has created an admin account for you. You can use it to log in:
admin@local.host
root
5iveL!fe
**Important Note:**
......
......@@ -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.
......@@ -8,23 +8,38 @@ Table of Contents
**[GitLab Flavored Markdown](#gitlab-flavored-markdown-gfm)**
[Newlines](#newlines)
[Multiple underscores in words](#multiple-underscores-in-words)
[URL autolinking](#url-autolinking)
[Code and Syntax Highlighting](#code-and-syntax-highlighting)
[Emoji](#emoji)
[Special GitLab references](#special-gitlab-references)
**[Standard Markdown](#standard-markdown)**
[Headers](#headers)
[Emphasis](#emphasis)
[Lists](#lists)
[Links](#links)
[Images](#images)
[Blockquotes](#blockquotes)
[Inline HTML](#inline-html)
[Horizontal Rule](#horizontal-rule)
[Line Breaks](#line-breaks)
[Tables](#tables)
**[References](#references)**
......@@ -33,7 +48,8 @@ Table of Contents
GitLab Flavored Markdown (GFM)
==============================
For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality.
For GitLab we developed something we call "GitLab Flavored Markdown" (GFM).
It extends the standard Markdown in a few significant ways to add some useful functionality.
You can use GFM in
......@@ -51,16 +67,20 @@ Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#mar
Newlines
--------
The biggest difference that GFM introduces is in the handling of linebreaks. With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended.
GFM honors the markdown specification in how [paragraphs and line breaks are handled](http://daringfireball.net/projects/markdown/syntax#p).
The next paragraph contains two phrases separated by a single newline character:
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.:
Roses are red
Violets are blue
Sugar is sweet
Roses are red
Violets are blue
Sugar is sweet
Multiple underscores in words
-----------------------------
It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words.
......@@ -319,13 +339,13 @@ Strikethrough uses two tildes. ~~Scratch this.~~
## Links
There are two ways to create links.
There are two ways to create links, inline-style and reference-style.
[I'm an inline-style link](https://www.google.com)
[I'm a reference-style link][Arbitrary case-insensitive reference text]
[I'm a relative reference to a repository file](../blob/master/LICENSE)
[I'm a relative reference to a repository file](LICENSE)
[You can use numbers for reference-style link definitions][1]
......@@ -341,7 +361,7 @@ There are two ways to create links.
[I'm a reference-style link][Arbitrary case-insensitive reference text]
[I'm a relative reference to a repository file](../blob/master/LICENSE)
[I'm a relative reference to a repository file](LICENSE)
[You can use numbers for reference-style link definitions][1]
......@@ -353,6 +373,15 @@ Some text to show that the reference links can follow later.
[1]: http://slashdot.org
[link text itself]: http://www.reddit.com
**Note**
Relative links do not allow referencing project files in a wiki page or wiki page in a project file.
The reason for this is that, in GitLab, wiki is always a separate git repository. For example:
`[I'm a reference-style link][style]`
will point the link to `wikis/style` when the link is inside of a wiki markdown file.
## Images
Here's our logo (hover to see the title text):
......@@ -365,15 +394,15 @@ Some text to show that the reference links can follow later.
[logo]: assets/logo-white.png
Here's our logo (hover to see the title text):
Here's our logo:
Inline-style:
![alt text](/assets/logo-white.png "Logo Title Text 1")
![alt text](/assets/logo-white.png)
Reference-style:
![alt text][logo]
[logo]: /assets/logo-white.png "Logo Title Text 2"
[logo]: /assets/logo-white.png
## Blockquotes
......
......@@ -58,16 +58,14 @@ 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
......
......@@ -15,7 +15,7 @@ Please report suspected security vulnerabilities in private to support@gitlab.co
1. Acknowledge the issue to the researcher that disclosed it
1. Fix the issue on a feature branch, do this on the private GitLab development server and update the VERSION and CHANGELOG in this branch
1. Consider creating and testing workarounds
1. Create feature branches for the blog posts on GitLab.org and GitLab.com and link them from the code branch
1. Create feature branches for the blog post on GitLab.com and link them from the code branch
1. Merge the code feature branch into master
1. Cherry-pick the code into the latest stable branch
1. Create a git tag vX.X.X for CE and another patch release for EE
......
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.
......@@ -11,8 +11,8 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"created_at": "2012-07-21T07:30:54Z",
"event_name": "project_create",
"name": "StoreCloud",
"owner_email": "johnsmith@gmail.com"
"owner_name": "John Smit",
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
"path": "stormcloud",
"path_with_namespace": "jsmith/stormcloud",
"project_id": 74,
......@@ -26,7 +26,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"created_at": "2012-07-21T07:30:58Z",
"event_name": "project_destroy",
"name": "Underscore",
"owner_email": "johnsmith@gmail.com"
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
"path": "underscore",
"path_with_namespace": "jsmith/underscore",
......@@ -36,7 +36,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
**New Team Member:**
```ruby
```json
{
"created_at": "2012-07-21T07:30:56Z",
"event_name": "user_add_to_team",
......
# 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
......
# From 6.6 to 6.7
### 0. Backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
sudo service gitlab stop
### 2. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch --all
```
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
```
### 3. Update gitlab-shell (and its config)
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.9.1
```
### 4. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without ... postgres')
sudo -u git -H bundle install --without development test postgres --deployment
# PostgreSQL installations (note: the line below states '--without ... mysql')
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
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
# Update init.d script
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
# Update the logrotate configuration (keep logs for 90 days instead of 52 weeks)
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
```
### 5. Start application
sudo service gitlab start
sudo service nginx restart
### 6. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade is complete!
## Things went south? Revert to previous version (6.6)
### 1. Revert the code to the previous version
Follow the [`upgrade guide from 6.5 to 6.6`](6.5-to-6.6.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above.
# Use the shell commands below to convert a MySQL GitLab database to a PostgreSQL one.
```
git clone https://github.com/lanyrd/mysql-postgresql-converter.git
cd mysql-postgresql-converter
mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production
python db_converter.py databasename.mysql databasename.psql
psql -f databasename.psql -d gitlabhq_production
```
......@@ -18,10 +18,11 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```bash
cd /home/git/gitlab
sudo -u git -H git pull origin STABLE_BRANCH
sudo -u git -H git fetch --all
sudo -u git -H git checkout LATEST_TAG
```
Replace STABLE_BRANCH with the minor version you want to upgrade to, for example `6-3-stable`.
Replace LATEST_TAG with the latest GitLab tag you want to upgrade to, for example `v6.6.3`.
### 3. Update gitlab-shell if it is not the latest version
......
......@@ -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.
---
......
......@@ -74,7 +74,7 @@ Feature: Groups
When I visit group "Owned" members page
Then I should see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
Then I should not see the "Remove User From Group" button for "Mary Jane"
Then I should not see the "Remove User From Group" button for "John Doe"
@javascript
Scenario: Guest should be able to remove himself from group
......
......@@ -3,6 +3,7 @@ Feature: Create Project
A user with ability to create a project
Should be able to create a new one
@javascript
Scenario: User create a project
Given I sign in as a user
When I visit new project page
......
......@@ -95,3 +95,47 @@ Feature: Project Merge Requests
Given I visit merge request page "Bug NS-04"
And I leave a comment with a header containing "Comment with a header"
Then The comment with the header should not have an ID
# Toggling inline comments
@javascript
Scenario: I hide comments on a merge request diff with comments in a single file
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
And I switch to the diff tab
And I leave a comment like "Line is wrong" on line 39 of the second file
And I click link "Hide inline discussion" of the second file
Then I should not see a comment like "Line is wrong" in the second file
@javascript
Scenario: I show comments on a merge request diff with comments in a single file
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
And I switch to the diff tab
And I leave a comment like "Line is wrong" on line 39 of the second file
And I click link "Hide inline discussion" of the second file
And I click link "Show inline discussion" of the second file
Then I should see a comment like "Line is wrong" in the second file
@javascript
Scenario: I hide comments on a merge request diff with comments in multiple files
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
And I switch to the diff tab
And I leave a comment like "Line is correct" on line 12 of the first file
And I leave a comment like "Line is wrong" on line 39 of the second file
And I click link "Hide inline discussion" of the second file
Then I should not see a comment like "Line is wrong" in the second file
And I should still see a comment like "Line is correct" in the first file
@javascript
Scenario: I show comments on a merge request diff with comments in multiple files
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
And I switch to the diff tab
And I leave a comment like "Line is correct" on line 12 of the first file
And I leave a comment like "Line is wrong" on line 39 of the second file
And I click link "Hide inline discussion" of the second file
And I click link "Show inline discussion" of the second file
Then I should see a comment like "Line is wrong" in the second file
And I should still see a comment like "Line is correct" in the first file
......@@ -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
......
......@@ -8,8 +8,8 @@ class CreateProject < Spinach::FeatureSteps
end
Then 'I should see project page' do
current_path.should == project_path(Project.last)
page.should have_content "Empty"
current_path.should == project_path(Project.last)
end
And 'I should see empty project instuctions' do
......
......@@ -203,7 +203,7 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
end
And 'I add various links to the wiki page' do
fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](doc/api/README.md)\n[Rake tasks](doc/raketasks)\n"
fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n"
fill_in "wiki[message]", with: "Adding links to wiki"
click_button "Create page"
end
......@@ -242,8 +242,8 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
end
Then 'I see Gitlab API document' do
current_path.should == project_blob_path(@project, "master/doc/api/README.md")
page.should have_content "Status codes"
current_path.should == project_wiki_path(@project, "api")
page.should have_content "Editing"
end
And 'I click on Rake tasks link' do
......@@ -251,9 +251,8 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
end
Then 'I see Rake tasks directory' do
current_path.should == project_tree_path(@project, "master/doc/raketasks")
page.should have_content "backup_restore.md"
page.should have_content "maintenance.md"
current_path.should == project_wiki_path(@project, "raketasks")
page.should have_content "Editing"
end
Given 'I go directory which contains README file' do
......
......@@ -182,6 +182,62 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end
end
step 'I click link "Hide inline discussion" of the second file' do
within '.files [id^=diff]:nth-child(2)' do
click_link "Diff comments"
end
end
step 'I click link "Show inline discussion" of the second file' do
within '.files [id^=diff]:nth-child(2)' do
click_link "Diff comments"
end
end
step 'I should not see a comment like "Line is wrong" in the second file' do
within '.files [id^=diff]:nth-child(2)' do
page.should_not have_visible_content "Line is wrong"
end
end
step 'I should see a comment like "Line is wrong" in the second file' do
within '.files [id^=diff]:nth-child(2) .note-text' do
page.should have_visible_content "Line is wrong"
end
end
step 'I leave a comment like "Line is correct" on line 12 of the first file' do
init_diff_note_first_file
within(".js-discussion-note-form") do
fill_in "note_note", with: "Line is correct"
click_button "Add Comment"
end
within ".files [id^=diff]:nth-child(1) .note-text" do
page.should have_content "Line is correct"
end
end
step 'I leave a comment like "Line is wrong" on line 39 of the second file' do
init_diff_note_second_file
within(".js-discussion-note-form") do
fill_in "note_note", with: "Line is wrong"
click_button "Add Comment"
end
within ".files [id^=diff]:nth-child(2) .note-text" do
page.should have_content "Line is wrong"
end
end
step 'I should still see a comment like "Line is correct" in the first file' do
within '.files [id^=diff]:nth-child(1) .note-text' do
page.should have_visible_content "Line is correct"
end
end
def project
@project ||= Project.find_by!(name: "Shop")
end
......@@ -204,4 +260,16 @@ class ProjectMergeRequests < Spinach::FeatureSteps
page.should have_content message
end
end
def init_diff_note_first_file
find('a[data-line-code="a5cc2925ca8258af241be7e5b0381edf30266302_12_12"]').click
end
def init_diff_note_second_file
find('a[data-line-code="8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d_28_39"]').click
end
def have_visible_content (text)
have_css("*", text: text, visible: true)
end
end
......@@ -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
......@@ -38,6 +38,7 @@ module API
mount Internal
mount SystemHooks
mount ProjectSnippets
mount ProjectMembers
mount DeployKeys
mount ProjectHooks
mount Services
......
......@@ -15,7 +15,7 @@ module API
end
class UserSafe < Grape::Entity
expose :name
expose :name, :username
end
class UserBasic < Grape::Entity
......@@ -44,7 +44,7 @@ module API
expose :id, :description, :default_branch
expose :public?, as: :public
expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: Entities::UserBasic
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :name, :name_with_namespace
expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
......@@ -58,18 +58,6 @@ module API
end
end
class TeamMember < UserBasic
expose :permission, as: :access_level do |user, options|
options[:user_team].user_team_user_relationships.find_by(user_id: user.id).permission
end
end
class TeamProject < Project
expose :greatest_access, as: :greatest_access_level do |project, options|
options[:user_team].user_team_project_relationships.find_by(project_id: project.id).greatest_access
end
end
class Group < Grape::Entity
expose :id, :name, :path, :owner_id
end
......@@ -144,7 +132,7 @@ module API
end
class MergeRequest < ProjectEntity
expose :target_branch, :source_branch, :title, :state, :upvotes, :downvotes
expose :target_branch, :source_branch, :title, :state, :upvotes, :downvotes, :description
expose :author, :assignee, using: Entities::UserBasic
expose :source_project_id, :target_project_id
end
......@@ -175,5 +163,33 @@ module API
class Namespace < Grape::Entity
expose :id, :path, :kind
end
class ProjectAccess < Grape::Entity
expose :project_access, as: :access_level
expose :notification_level
end
class GroupAccess < Grape::Entity
expose :group_access, as: :access_level
expose :notification_level
end
class ProjectWithAccess < Project
expose :permissions do
expose :project_access, using: Entities::ProjectAccess do |project, options|
project.users_projects.find_by(user_id: options[:user].id)
end
expose :group_access, using: Entities::GroupAccess do |project, options|
if project.group
project.group.users_groups.find_by(user_id: options[:user].id)
end
end
end
end
class Label < Grape::Entity
expose :name
end
end
end
......@@ -47,7 +47,7 @@ module API
end
def find_project(id)
project = Project.find_by(id: id) || Project.find_with_namespace(id)
project = Project.find_with_namespace(id) || Project.find_by(id: id)
if project && can?(current_user, :read_project, project)
project
......
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,37 +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
return false if user.ldap_user? && Gitlab::LDAP::User.blocked?(user.extern_uid)
actor = if params[:key_id]
Key.find(params[:key_id])
elsif params[:user_id]
User.find(params[:user_id])
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
return false unless actor
user.can?(action, project)
end
Gitlab::GitAccess.new.allowed?(
actor,
params[:action],
project,
params[:ref],
params[:oldrev],
params[:newrev]
)
end
#
......
......@@ -64,6 +64,7 @@ module API
# target_project - The target project of the merge request defaults to the :id of the project
# assignee_id - Assignee user ID
# title (required) - Title of MR
# description - Description of MR
#
# Example:
# POST /projects/:id/merge_requests
......@@ -72,7 +73,7 @@ module API
set_current_user_for_thread do
authorize! :write_merge_request, user_project
required_attributes! [:source_branch, :target_branch, :title]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]
merge_request = user_project.merge_requests.new(attrs)
merge_request.author = current_user
merge_request.source_project = user_project
......@@ -105,12 +106,13 @@ module API
# assignee_id - Assignee user ID
# title - Title of MR
# state_event - Status of MR. (close|reopen|merge)
# description - Description of MR
# Example:
# PUT /projects/:id/merge_request/:merge_request_id
#
put ":id/merge_request/:merge_request_id" do
set_current_user_for_thread do
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :modify_merge_request, merge_request
......
......@@ -5,15 +5,6 @@ module API
before { authorize_admin_project }
resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
end
# Get project hooks
#
# Parameters:
......
module API
# Projects members API
class ProjectMembers < Grape::API
before { authenticate! }
resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
end
# Get a project team members
#
# Parameters:
# id (required) - The ID of a project
# query - Query string
# Example Request:
# GET /projects/:id/members
get ":id/members" do
if params[:query].present?
@members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%")
else
@members = paginate user_project.users
end
present @members, with: Entities::ProjectMember, project: user_project
end
# Get a project team members
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a user
# Example Request:
# GET /projects/:id/members/:user_id
get ":id/members/:user_id" do
@member = user_project.users.find params[:user_id]
present @member, with: Entities::ProjectMember, project: user_project
end
# Add a new project team member
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a user
# access_level (required) - Project access level
# Example Request:
# POST /projects/:id/members
post ":id/members" do
authorize! :admin_project, user_project
required_attributes! [:user_id, :access_level]
# either the user is already a team member or a new one
team_member = user_project.team_member_by_id(params[:user_id])
if team_member.nil?
team_member = user_project.users_projects.new(
user_id: params[:user_id],
project_access: params[:access_level]
)
end
if team_member.save
@member = team_member.user
present @member, with: Entities::ProjectMember, project: user_project
else
handle_project_member_errors team_member.errors
end
end
# Update project team member
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a team member
# access_level (required) - Project access level
# Example Request:
# PUT /projects/:id/members/:user_id
put ":id/members/:user_id" do
authorize! :admin_project, user_project
required_attributes! [:access_level]
team_member = user_project.users_projects.find_by(user_id: params[:user_id])
not_found!("User can not be found") if team_member.nil?
if team_member.update_attributes(project_access: params[:access_level])
@member = team_member.user
present @member, with: Entities::ProjectMember, project: user_project
else
handle_project_member_errors team_member.errors
end
end
# Remove a team member from project
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a team member
# Example Request:
# DELETE /projects/:id/members/:user_id
delete ":id/members/:user_id" do
authorize! :admin_project, user_project
team_member = user_project.users_projects.find_by(user_id: params[:user_id])
unless team_member.nil?
team_member.destroy
else
{message: "Access revoked", id: params[:user_id].to_i}
end
end
end
end
end
......@@ -5,13 +5,6 @@ module API
resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
def map_public_to_visibility_level(attrs)
publik = attrs.delete(:public)
publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik)
......@@ -55,7 +48,7 @@ module API
# Example Request:
# GET /projects/:id
get ":id" do
present user_project, with: Entities::Project
present user_project, with: Entities::ProjectWithAccess, user: current_user
end
# Get a single project events
......@@ -196,104 +189,6 @@ module API
user_project.forked_project_link.destroy
end
end
# Get a project team members
#
# Parameters:
# id (required) - The ID of a project
# query - Query string
# Example Request:
# GET /projects/:id/members
get ":id/members" do
if params[:query].present?
@members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%")
else
@members = paginate user_project.users
end
present @members, with: Entities::ProjectMember, project: user_project
end
# Get a project team members
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a user
# Example Request:
# GET /projects/:id/members/:user_id
get ":id/members/:user_id" do
@member = user_project.users.find params[:user_id]
present @member, with: Entities::ProjectMember, project: user_project
end
# Add a new project team member
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a user
# access_level (required) - Project access level
# Example Request:
# POST /projects/:id/members
post ":id/members" do
authorize! :admin_project, user_project
required_attributes! [:user_id, :access_level]
# either the user is already a team member or a new one
team_member = user_project.team_member_by_id(params[:user_id])
if team_member.nil?
team_member = user_project.users_projects.new(
user_id: params[:user_id],
project_access: params[:access_level]
)
end
if team_member.save
@member = team_member.user
present @member, with: Entities::ProjectMember, project: user_project
else
handle_project_member_errors team_member.errors
end
end
# Update project team member
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a team member
# access_level (required) - Project access level
# Example Request:
# PUT /projects/:id/members/:user_id
put ":id/members/:user_id" do
authorize! :admin_project, user_project
required_attributes! [:access_level]
team_member = user_project.users_projects.find_by(user_id: params[:user_id])
not_found!("User can not be found") if team_member.nil?
if team_member.update_attributes(project_access: params[:access_level])
@member = team_member.user
present @member, with: Entities::ProjectMember, project: user_project
else
handle_project_member_errors team_member.errors
end
end
# Remove a team member from project
#
# Parameters:
# id (required) - The ID of a project
# user_id (required) - The ID of a team member
# Example Request:
# DELETE /projects/:id/members/:user_id
delete ":id/members/:user_id" do
authorize! :admin_project, user_project
team_member = user_project.users_projects.find_by(user_id: params[:user_id])
unless team_member.nil?
team_member.destroy
else
{message: "Access revoked", id: params[:user_id].to_i}
end
end
# search for projects current_user has access to
#
# Parameters:
......@@ -320,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'] = ""
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,36 +40,35 @@ 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'
if gitlab_ci_request?(login, password)
return @app.call(env)
end
end
@user = authenticate_user(login, password)
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
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
:push_code
end
return false unless can?(user, action, project)
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?
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
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 project_by_path(path)
if m = /^([\w\.\/-]+)\.git/.match(path).to_a
path_with_namespace = m.last
path_with_namespace.gsub!(/\.wiki$/, '')
def parse_refs
input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
Zlib::GzipReader.new(@request.body).read
else
@request.body.read
Project.find_with_namespace(path_with_namespace)
end
# Need to reset seek point
@request.body.rewind
# Parse refs
refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
# Cleanup grabare from refs
# if push to multiple branches
refs.map do |ref|
ref.gsub(/00.*/, "")
end
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
......@@ -2,6 +2,12 @@ module Gitlab
class Shell
class AccessDenied < StandardError; end
class KeyAdder < Struct.new(:io)
def add_key(id, key)
io.puts("#{id}\t#{key.strip}")
end
end
# Init new repository
#
# name - project path with namespace
......@@ -130,6 +136,16 @@ module Gitlab
system "#{gitlab_shell_path}/bin/gitlab-keys", "add-key", key_id, key_content
end
# Batch-add keys to authorized_keys
#
# Ex.
# batch_add_keys { |adder| adder.add_key("key-42", "sha-rsa ...") }
def batch_add_keys(&block)
IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys batch-add-keys), 'w') do |io|
block.call(KeyAdder.new(io))
end
end
# Remove ssh key from gitlab shell
#
# Ex.
......
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
module Gitlab
module LDAP
class Access
attr_reader :adapter
def self.open(&block)
Gitlab::LDAP::Adapter.open do |adapter|
block.call(self.new(adapter))
end
end
def initialize(adapter=nil)
@adapter = adapter
end
def allowed?(user)
!!Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter)
rescue
false
end
end
end
end
module Gitlab
module LDAP
class Adapter
attr_reader :ldap
def self.open(&block)
Net::LDAP.open(adapter_options) do |ldap|
block.call(self.new(ldap))
end
end
def self.config
Gitlab.config.ldap
end
def self.adapter_options
encryption = config['method'].to_s == 'ssl' ? :simple_tls : nil
options = {
host: config['host'],
port: config['port'],
encryption: encryption
}
auth_options = {
auth: {
method: :simple,
username: config['bind_dn'],
password: config['password']
}
}
if config['password'] || config['bind_dn']
options.merge!(auth_options)
end
options
end
def initialize(ldap=nil)
@ldap = ldap || Net::LDAP.new(self.class.adapter_options)
end
def users(field, value)
if field.to_sym == :dn
options = {
base: value
}
else
options = {
base: config['base'],
filter: Net::LDAP::Filter.eq(field, value)
}
end
if config['user_filter'].present?
user_filter = Net::LDAP::Filter.construct(config['user_filter'])
options[:filter] = if options[:filter]
Net::LDAP::Filter.join(options[:filter], user_filter)
else
user_filter
end
end
entries = ldap.search(options).select do |entry|
entry.respond_to? config.uid
end
entries.map do |entry|
Gitlab::LDAP::Person.new(entry)
end
end
def user(*args)
users(*args).first
end
private
def config
@config ||= self.class.config
end
end
end
end
module Gitlab
module LDAP
class Person
def self.find_by_uid(uid, adapter=nil)
adapter ||= Gitlab::LDAP::Adapter.new
adapter.user(config.uid, uid)
end
def self.find_by_dn(dn, adapter=nil)
adapter ||= Gitlab::LDAP::Adapter.new
adapter.user('dn', dn)
end
def initialize(entry)
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
@entry = entry
end
def name
entry.cn.first
end
def uid
entry.send(config.uid).first
end
def username
uid
end
def dn
entry.dn
end
private
def entry
@entry
end
def adapter
@adapter ||= Gitlab::LDAP::Adapter.new
end
def config
@config ||= Gitlab.config.ldap
end
end
end
end
......@@ -13,8 +13,8 @@ module Gitlab
def find_or_create(auth)
@auth = auth
if uid.blank? || email.blank?
raise_error("Account must provide an uid and email address")
if uid.blank? || email.blank? || username.blank?
raise_error("Account must provide a dn, uid and email address")
end
user = find(auth)
......@@ -62,8 +62,16 @@ module Gitlab
return nil unless ldap_conf.enabled && login.present? && password.present?
ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
filter = Net::LDAP::Filter.eq(ldap.uid, login)
# Apply LDAP user filter if present
if ldap_conf['user_filter'].present?
user_filter = Net::LDAP::Filter.construct(ldap_conf['user_filter'])
filter = Net::LDAP::Filter.join(filter, user_filter)
end
ldap_user = ldap.bind_as(
filter: Net::LDAP::Filter.eq(ldap.uid, login),
filter: filter,
size: 1,
password: password
)
......@@ -71,22 +79,20 @@ module Gitlab
find_by_uid(ldap_user.dn) if ldap_user
end
# Check LDAP user existance by dn. User in git over ssh check
#
# It covers 2 cases:
# * when ldap account was removed
# * when ldap account was deactivated by change of OU membership in 'dn'
def blocked?(dn)
ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
ldap.connection.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, size: 1).blank?
end
private
def find_by_uid(uid)
model.where(provider: provider, extern_uid: uid).last
end
def username
(auth.info.nickname || samaccountname).to_s.force_encoding("utf-8")
end
def samaccountname
(auth.extra[:raw_info][:samaccountname] || []).first
end
def provider
'ldap'
end
......
......@@ -98,7 +98,7 @@ module Gitlab
(?<prefix>\W)? # Prefix
( # Reference
@(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name
|\#(?<issue>([a-zA-Z]+-)?\d+) # Issue ID
|\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
|!(?<merge_request>\d+) # MR ID
|\$(?<snippet>\d+) # Snippet ID
|(?<commit>[\h]{6,40}) # Commit ID
......@@ -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
......
......@@ -7,7 +7,7 @@ module Gitlab
end
def project_name_regex
/\A[a-zA-Z0-9][a-zA-Z0-9_\-\. ]*\z/
/\A[a-zA-Z0-9_][a-zA-Z0-9_\-\. ]*\z/
end
def name_regex
......@@ -49,7 +49,7 @@ module Gitlab
protected
def default_regex
/\A[.?]?[a-zA-Z0-9][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
/\A[.?]?[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
end
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
......
module Gitlab
class Seeder
def self.quiet
mute_mailer
SeedFu.quiet = true
yield
SeedFu.quiet = false
puts "\nOK".green
end
def self.by_user(user)
begin
Thread.current[:current_user] = user
yield
ensure
Thread.current[:current_user] = nil
end
end
def self.mute_mailer
code = <<-eos
def Notify.delay
self
end
eos
eval(code)
end
end
end
require_relative "popen"
require_relative "version_info"
module Gitlab
......
......@@ -46,8 +46,10 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
end
def preprocess(full_document)
if @project
h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?)
if is_wiki?
full_document
elsif @project
h.create_relative_links(full_document, @project, @ref, @request_path)
else
full_document
end
......
......@@ -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.
......
......@@ -2,21 +2,19 @@
# based on: http://stackoverflow.com/a/4883967
/home/git/gitlab/log/*.log {
weekly
daily
missingok
rotate 52
rotate 90
compress
delaycompress
notifempty
copytruncate
}
/home/git/gitlab-shell/gitlab-shell.log {
weekly
daily
missingok
rotate 52
rotate 90
compress
delaycompress
notifempty
copytruncate
}
......@@ -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;
}
task dev: ["dev:setup"]
namespace :dev do
desc "GITLAB | Setup developer environment (db, fixtures)"
task :setup => :environment do
ENV['force'] = 'yes'
Rake::Task["db:setup"].invoke
Rake::Task["db:seed_fu"].invoke
Rake::Task["gitlab:setup"].invoke
Rake::Task["gitlab:shell:setup"].invoke
end
end
......@@ -17,6 +17,7 @@ namespace :gitlab do
check_database_config_exists
check_database_is_not_sqlite
check_migrations_are_up
check_orphaned_users_groups
check_gitlab_config_exists
check_gitlab_config_not_outdated
check_log_writable
......@@ -65,6 +66,7 @@ namespace :gitlab do
puts "no".green
else
puts "yes".red
puts "Please fix this by removing the SQLite entry from the database.yml".blue
for_more_information(
"https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL",
see_database_guide
......@@ -181,6 +183,19 @@ namespace :gitlab do
end
end
def check_orphaned_users_groups
print "Database contains orphaned UsersGroups? ... "
if UsersGroup.where("user_id not in (select id from users)").count > 0
puts "yes".red
try_fixing_it(
"You can delete the orphaned records using something along the lines of:",
sudo_gitlab("bundle exec rails runner -e production 'UsersGroup.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'")
)
else
puts "no".green
end
end
def check_satellites_exist
print "Projects have satellites? ... "
......@@ -727,7 +742,7 @@ namespace :gitlab do
end
def check_gitlab_shell
required_version = Gitlab::VersionInfo.new(1, 7, 9)
required_version = Gitlab::VersionInfo.new(1, 9, 1)
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_version} ? ... "
......
......@@ -15,6 +15,14 @@ namespace :gitlab do
end
Rake::Task["db:setup"].invoke
config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
success = case config["adapter"]
when /^mysql/ then
Rake::Task["add_limits_mysql"].invoke
when "postgresql" then
end
Rake::Task["db:seed_fu"].invoke
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".red
......
......@@ -34,14 +34,18 @@ namespace :gitlab do
Gitlab::Shell.new.remove_all_keys
Gitlab::Shell.new.batch_add_keys do |adder|
Key.find_each(batch_size: 1000) do |key|
if Gitlab::Shell.new.add_key(key.shell_id, key.key)
adder.add_key(key.shell_id, key.key)
print '.'
else
print 'F'
end
end
unless $?.success?
puts "Failed to add keys...".red
exit 1
end
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".red
exit 1
......
......@@ -82,6 +82,8 @@ namespace :gitlab do
def run(command)
output, _ = Gitlab::Popen.popen(command)
output
rescue Errno::ENOENT
'' # if the command does not exist, return an empty string
end
def uid_for(user_name)
......
......@@ -2,15 +2,13 @@ namespace :gitlab do
desc "GITLAB | Run all tests"
task :test do
cmds = [
%W(rake db:setup),
%W(rake db:seed_fu),
%W(rake spinach),
%W(rake spec),
%W(rake jasmine:ci)
]
cmds.each do |cmd|
system({'RAILS_ENV' => 'test'}, *cmd)
system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
raise "#{cmd} failed!" unless $?.exitstatus.zero?
end
......
desc "GITLAB | Add limits to strings in mysql database"
task add_limits_mysql: :environment do
puts "Adding limits to schema.rb for mysql"
LimitsToMysql.new.up
end
class LimitsToMysql < ActiveRecord::Migration
def up
change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
change_column :snippets, :content, :text, limit: 2147483647
change_column :notes, :st_diff, :text, limit: 2147483647
end
end
Rake::Task["spec"].clear if Rake::Task.task_defined?('spec')
desc "GITLAB | Run specs"
task :spec do
cmds = [
%W(rake gitlab:setup),
%W(rspec spec),
]
cmds.each do |cmd|
system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
raise "#{cmd} failed!" unless $?.exitstatus.zero?
end
end
Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach')
desc "GITLAB | Run spinach"
task :spinach do
cmds = [
%W(rake gitlab:setup),
%W(spinach),
]
cmds.each do |cmd|
system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
raise "#{cmd} failed!" unless $?.exitstatus.zero?
end
end
Rake::Task["test"].clear
desc "GITLAB | Run all tests"
task :test do
Rake::Task["gitlab:test"].invoke
end
......@@ -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
......
......@@ -3,11 +3,13 @@ require 'spec_helper'
describe MergeRequestsFinder do
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) }
......
......@@ -440,12 +440,6 @@ describe GitlabMarkdownHelper do
markdown(actual).should match(expected)
end
it "should handle wiki urls" do
actual = "[Link](test/link)\n"
expected = "<p><a href=\"/#{project.path_with_namespace}/wikis/test/link\">Link</a></p>\n"
markdown(actual).should match(expected)
end
it "should handle relative urls in reference links for a file in master" do
actual = "[GitLab API doc][GitLab readme]\n [GitLab readme]: doc/api/README.md\n"
expected = "<p><a href=\"/#{project.path_with_namespace}/blob/master/doc/api/README.md\">GitLab API doc</a></p>\n"
......
......@@ -9,7 +9,8 @@ describe Gitlab::LDAP do
@info = double(
uid: '12djsak321',
name: 'John',
email: 'john@mail.com'
email: 'john@mail.com',
nickname: 'john'
)
end
......
......@@ -146,7 +146,8 @@ describe Notify do
end
context 'for issues' do
let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project ) }
let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project) }
let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: Faker::Lorem.sentence) }
describe 'that are new' do
subject { Notify.new_issue_email(issue.assignee_id, issue.id) }
......@@ -162,6 +163,14 @@ describe Notify do
end
end
describe 'that are new with a description' do
subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) }
it 'contains the description' do
should have_body_text /#{issue_with_description.description}/
end
end
describe 'that have been reassigned' do
subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) }
......@@ -220,7 +229,9 @@ 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) }
describe 'that are new' do
subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
......@@ -244,6 +255,14 @@ describe Notify do
end
end
describe 'that are new with a description' do
subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) }
it 'contains the description' do
should have_body_text /#{merge_request_with_description.description}/
end
end
describe 'that are reassigned' do
subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
......@@ -270,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
......@@ -335,10 +377,6 @@ describe Notify do
should deliver_to recipient.email
end
it 'contains the name of the note\'s author' do
should have_body_text /#{note_author.name}/
end
it 'contains the message from the note' do
should have_body_text /#{note.note}/
end
......@@ -468,6 +506,8 @@ describe Notify do
let(:example_site_path) { root_path }
let(:user) { create(:user) }
let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, 'cd5c4bac', 'b1e6a9db') }
let(:commits) { Commit.decorate(compare.commits) }
let(:diff_path) { project_compare_path(project, from: commits.first, to: commits.last) }
subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) }
......@@ -492,5 +532,9 @@ describe Notify do
it 'includes diffs' do
should have_body_text /Checkout wiki pages for installation information/
end
it 'contains a link to the diff' do
should have_body_text /#{diff_path}/
end
end
end
require 'spec_helper'
describe ProjectHook do
describe '.push_hooks' do
it 'should return hooks for push events only' do
hook = create(:project_hook, push_events: true)
hook2 = create(:project_hook, push_events: false)
expect(ProjectHook.push_hooks).to eq([hook])
end
end
describe '.tag_push_hooks' do
it 'should return hooks for tag push events only' do
hook = create(:project_hook, tag_push_events: true)
hook2 = create(:project_hook, tag_push_events: false)
expect(ProjectHook.tag_push_hooks).to eq([hook])
end
end
end
......@@ -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
......@@ -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
......
......@@ -47,30 +47,6 @@ describe UsersProjectObserver do
end
describe "#after_create" do
context 'wiki_enabled creates repository directory' do
context 'wiki_enabled true creates wiki repository directory' do
before do
@project = create(:project, wiki_enabled: true)
@path = GollumWiki.new(@project, user).send(:path_to_repo)
end
after do
FileUtils.rm_rf(@path)
end
it { File.exists?(@path).should be_true }
end
context 'wiki_enabled false does not create wiki repository directory' do
before do
@project = create(:project, wiki_enabled: false)
@path = GollumWiki.new(@project, user).send(:path_to_repo)
end
it { File.exists?(@path).should be_false }
end
end
it "should send email to user" do
subject.should_receive(:notification)
Event.stub(create: true)
......
......@@ -92,9 +92,10 @@ describe API::API do
it "should return merge_request" do
post api("/projects/#{fork_project.id}/merge_requests", user2),
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id
title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
response.status.should == 201
json_response['title'].should == 'Test merge_request'
json_response['description'].should == 'Test description for Test merge_request'
end
it "should not return 422 when source_branch equals target_branch" do
......@@ -168,6 +169,12 @@ describe API::API do
json_response['title'].should == 'New title'
end
it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), description: "New description"
response.status.should == 200
json_response['description'].should == 'New description'
end
it "should return 422 when source_branch and target_branch are renamed the same" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
......
require 'spec_helper'
describe API::API do
include ApiHelpers
before(:each) { enable_observers }
after(:each) { disable_observers }
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
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) }
describe "GET /projects/:id/members" do
before { users_project }
before { users_project2 }
it "should return project team members" do
get api("/projects/#{project.id}/members", user)
response.status.should == 200
json_response.should be_an Array
json_response.count.should == 2
json_response.map { |u| u['email'] }.should include user.email
end
it "finds team members with query string" do
get api("/projects/#{project.id}/members", user), query: user.username
response.status.should == 200
json_response.should be_an Array
json_response.count.should == 1
json_response.first['email'].should == user.email
end
it "should return a 404 error if id not found" do
get api("/projects/9999/members", user)
response.status.should == 404
end
end
describe "GET /projects/:id/members/:user_id" do
before { users_project }
it "should return project team member" do
get api("/projects/#{project.id}/members/#{user.id}", user)
response.status.should == 200
json_response['email'].should == user.email
json_response['access_level'].should == UsersProject::MASTER
end
it "should return a 404 error if user id not found" do
get api("/projects/#{project.id}/members/1234", user)
response.status.should == 404
end
end
describe "POST /projects/:id/members" do
it "should add user to project team" do
expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
}.to change { UsersProject.count }.by(1)
response.status.should == 201
json_response['email'].should == user2.email
json_response['access_level'].should == UsersProject::DEVELOPER
end
it "should return a 201 status if user is already project member" do
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
}.not_to change { UsersProject.count }.by(1)
response.status.should == 201
json_response['email'].should == user2.email
json_response['access_level'].should == UsersProject::DEVELOPER
end
it "should return a 400 error when user id is not given" do
post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER
response.status.should == 400
end
it "should return a 400 error when access level is not given" do
post api("/projects/#{project.id}/members", user), user_id: user2.id
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234
response.status.should == 422
end
end
describe "PUT /projects/:id/members/:user_id" do
before { users_project2 }
it "should update project team member" do
put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: UsersProject::MASTER
response.status.should == 200
json_response['email'].should == user3.email
json_response['access_level'].should == UsersProject::MASTER
end
it "should return a 404 error if user_id is not found" do
put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER
response.status.should == 404
end
it "should return a 400 error when access level is not given" do
put api("/projects/#{project.id}/members/#{user3.id}", user)
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123
response.status.should == 422
end
end
describe "DELETE /projects/:id/members/:user_id" do
before { users_project }
before { users_project2 }
it "should remove user from project team" do
expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to change { UsersProject.count }.by(-1)
end
it "should return 200 if team member is not part of a project" do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to_not change { UsersProject.count }.by(1)
end
it "should return 200 if team member already removed" do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
delete api("/projects/#{project.id}/members/#{user3.id}", user)
response.status.should == 200
end
it "should return 200 OK when the user was not member" do
expect {
delete api("/projects/#{project.id}/members/1000000", user)
}.to change { UsersProject.count }.by(0)
response.status.should == 200
json_response['message'].should == "Access revoked"
json_response['id'].should == 1000000
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
......@@ -259,6 +260,7 @@ describe API::API do
describe "GET /projects/:id" do
before { project }
before { users_project }
it "should return a project by id" do
get api("/projects/#{project.id}", user)
......@@ -284,6 +286,28 @@ describe API::API do
get api("/projects/#{project.id}", other_user)
response.status.should == 404
end
describe 'permissions' do
context 'personal project' do
before { get api("/projects/#{project.id}", user) }
it { response.status.should == 200 }
it { json_response['permissions']["project_access"]["access_level"].should == Gitlab::Access::MASTER }
it { json_response['permissions']["group_access"].should be_nil }
end
context 'group project' do
before do
project2 = create(:project, group: create(:group))
project2.group.add_owner(user)
get api("/projects/#{project2.id}", user)
end
it { response.status.should == 200 }
it { json_response['permissions']["project_access"].should be_nil }
it { json_response['permissions']["group_access"]["access_level"].should == Gitlab::Access::OWNER }
end
end
end
describe "GET /projects/:id/events" do
......@@ -311,148 +335,6 @@ describe API::API do
end
end
describe "GET /projects/:id/members" do
before { users_project }
before { users_project2 }
it "should return project team members" do
get api("/projects/#{project.id}/members", user)
response.status.should == 200
json_response.should be_an Array
json_response.count.should == 2
json_response.map { |u| u['email'] }.should include user.email
end
it "finds team members with query string" do
get api("/projects/#{project.id}/members", user), query: user.username
response.status.should == 200
json_response.should be_an Array
json_response.count.should == 1
json_response.first['email'].should == user.email
end
it "should return a 404 error if id not found" do
get api("/projects/9999/members", user)
response.status.should == 404
end
end
describe "GET /projects/:id/members/:user_id" do
before { users_project }
it "should return project team member" do
get api("/projects/#{project.id}/members/#{user.id}", user)
response.status.should == 200
json_response['email'].should == user.email
json_response['access_level'].should == UsersProject::MASTER
end
it "should return a 404 error if user id not found" do
get api("/projects/#{project.id}/members/1234", user)
response.status.should == 404
end
end
describe "POST /projects/:id/members" do
it "should add user to project team" do
expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
}.to change { UsersProject.count }.by(1)
response.status.should == 201
json_response['email'].should == user2.email
json_response['access_level'].should == UsersProject::DEVELOPER
end
it "should return a 201 status if user is already project member" do
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
}.not_to change { UsersProject.count }.by(1)
response.status.should == 201
json_response['email'].should == user2.email
json_response['access_level'].should == UsersProject::DEVELOPER
end
it "should return a 400 error when user id is not given" do
post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER
response.status.should == 400
end
it "should return a 400 error when access level is not given" do
post api("/projects/#{project.id}/members", user), user_id: user2.id
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234
response.status.should == 422
end
end
describe "PUT /projects/:id/members/:user_id" do
before { users_project2 }
it "should update project team member" do
put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: UsersProject::MASTER
response.status.should == 200
json_response['email'].should == user3.email
json_response['access_level'].should == UsersProject::MASTER
end
it "should return a 404 error if user_id is not found" do
put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER
response.status.should == 404
end
it "should return a 400 error when access level is not given" do
put api("/projects/#{project.id}/members/#{user3.id}", user)
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123
response.status.should == 422
end
end
describe "DELETE /projects/:id/members/:user_id" do
before { users_project }
before { users_project2 }
it "should remove user from project team" do
expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to change { UsersProject.count }.by(-1)
end
it "should return 200 if team member is not part of a project" do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to_not change { UsersProject.count }.by(1)
end
it "should return 200 if team member already removed" do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
delete api("/projects/#{project.id}/members/#{user3.id}", user)
response.status.should == 200
end
it "should return 200 OK when the user was not member" do
expect {
delete api("/projects/#{project.id}/members/1000000", user)
}.to change { UsersProject.count }.by(0)
response.status.should == 200
json_response['message'].should == "Access revoked"
json_response['id'].should == 1000000
end
end
describe "GET /projects/:id/snippets" do
before { snippet }
......@@ -609,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)
......@@ -681,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
......@@ -751,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 GitTagPushService do
let (:user) { create :user }
let (:project) { create :project }
let (:service) { GitTagPushService.new }
before do
@ref = 'refs/tags/super-tag'
@oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81'
@newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb'
end
describe 'Git Tag Push Data' do
before do
service.execute(project, user, @oldrev, @newrev, @ref)
@push_data = service.push_data
end
subject { @push_data }
it { should include(ref: @ref) }
it { should include(before: @oldrev) }
it { should include(after: @newrev) }
it { should include(user_id: user.id) }
it { should include(user_name: user.name) }
it { should include(project_id: project.id) }
context 'With repository data' do
subject { @push_data[:repository] }
it { should include(name: project.name) }
it { should include(url: project.url_to_repo) }
it { should include(description: project.description) }
it { should include(homepage: project.web_url) }
end
end
describe "Web Hooks" do
context "execute web hooks" do
it "when pushing tags" do
project.should_receive(:execute_hooks)
service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0')
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
......
......@@ -38,6 +38,27 @@ describe Projects::CreateService do
it { @project.namespace.should == @group }
end
context 'wiki_enabled creates repository directory' do
context 'wiki_enabled true creates wiki repository directory' do
before do
@project = create_project(@user, @opts)
@path = GollumWiki.new(@project, @user).send(:path_to_repo)
end
it { File.exists?(@path).should be_true }
end
context 'wiki_enabled false does not create wiki repository directory' do
before do
@opts.merge!(wiki_enabled: false)
@project = create_project(@user, @opts)
@path = GollumWiki.new(@project, @user).send(:path_to_repo)
end
it { File.exists?(@path).should be_false }
end
end
context 'respect configured visibility setting' do
before(:each) do
@settings = double("settings")
......
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']
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'
end
# Loading more in this block will cause your tests to run faster. However,
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
require 'webmock/rspec'
require 'email_spec'
require 'sidekiq/testing/inline'
require 'capybara/poltergeist'
# 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
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}
# 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)
WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config|
RSpec.configure do |config|
config.mock_with :rspec
config.include LoginHelpers, type: :feature
......@@ -54,10 +47,4 @@ Spork.prefork do
config.before(:each) do
TestEnv.setup_stubs
end
end
end
Spork.each_run do
# This code will be run each time you run your specs.
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
......@@ -106,7 +105,7 @@ module TestEnv
setup_stubs
[
%W(git reset --hard --quiet),
%W(git clean -fx),
%W(git clean -fx --quiet),
%W(git checkout --quiet origin/master)
].each do |git_cmd|
system(*git_cmd, chdir: seed_satellite_path)
......@@ -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