summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG36
-rw-r--r--CONTRIBUTING.md41
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock27
-rw-r--r--PROCESS.md7
-rw-r--r--README.md2
-rw-r--r--app/assets/javascripts/markdown_area.js.coffee45
-rw-r--r--app/assets/javascripts/notes.js.coffee55
-rw-r--r--app/assets/stylesheets/generic/common.scss4
-rw-r--r--app/assets/stylesheets/generic/highlight.scss4
-rw-r--r--app/assets/stylesheets/generic/issue_box.scss5
-rw-r--r--app/assets/stylesheets/generic/markdown_area.scss26
-rw-r--r--app/assets/stylesheets/generic/mobile.scss17
-rw-r--r--app/assets/stylesheets/generic/timeline.scss17
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss31
-rw-r--r--app/assets/stylesheets/sections/events.scss10
-rw-r--r--app/assets/stylesheets/sections/issues.scss10
-rw-r--r--app/assets/stylesheets/sections/notes.scss17
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb10
-rw-r--r--app/controllers/projects/application_controller.rb27
-rw-r--r--app/controllers/projects/hooks_controller.rb1
-rw-r--r--app/controllers/projects/imports_controller.rb49
-rw-r--r--app/controllers/projects/issues_controller.rb16
-rw-r--r--app/controllers/projects/merge_requests_controller.rb15
-rw-r--r--app/controllers/projects/notes_controller.rb4
-rw-r--r--app/controllers/projects/repositories_controller.rb9
-rw-r--r--app/controllers/projects/services_controller.rb2
-rw-r--r--app/controllers/projects/snippets_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb50
-rw-r--r--app/controllers/snippets_controller.rb2
-rw-r--r--app/controllers/users_controller.rb7
-rw-r--r--app/finders/issuable_finder.rb9
-rw-r--r--app/helpers/blob_helper.rb11
-rw-r--r--app/helpers/events_helper.rb22
-rw-r--r--app/helpers/gitlab_markdown_helper.rb12
-rw-r--r--app/helpers/issues_helper.rb15
-rw-r--r--app/helpers/oauth_helper.rb4
-rw-r--r--app/helpers/profile_helper.rb6
-rw-r--r--app/helpers/sorting_helper.rb17
-rw-r--r--app/mailers/emails/profile.rb3
-rw-r--r--app/mailers/notify.rb8
-rw-r--r--app/models/commit.rb8
-rw-r--r--app/models/hooks/web_hook.rb8
-rw-r--r--app/models/identity.rb5
-rw-r--r--app/models/merge_request.rb14
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb33
-rw-r--r--app/models/project_services/hipchat_service.rb12
-rw-r--r--app/models/project_wiki.rb2
-rw-r--r--app/models/service.rb4
-rw-r--r--app/models/snippet.rb10
-rw-r--r--app/models/user.rb11
-rw-r--r--app/services/merge_requests/refresh_service.rb23
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/projects/create_service.rb50
-rw-r--r--app/services/test_hook_service.rb3
-rw-r--r--app/uploaders/attachment_uploader.rb4
-rw-r--r--app/views/admin/projects/index.html.haml8
-rw-r--r--app/views/admin/users/index.html.haml5
-rw-r--r--app/views/admin/users/show.html.haml2
-rw-r--r--app/views/dashboard/issues.atom.builder21
-rw-r--r--app/views/dashboard/projects.html.haml9
-rw-r--r--app/views/dashboard/show.atom.builder25
-rw-r--r--app/views/devise/sessions/_oauth_providers.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml8
-rw-r--r--app/views/explore/projects/index.html.haml8
-rw-r--r--app/views/groups/issues.atom.builder13
-rw-r--r--app/views/groups/show.atom.builder24
-rw-r--r--app/views/layouts/_search.html.haml11
-rw-r--r--app/views/profiles/show.html.haml2
-rw-r--r--app/views/projects/_issuable_filter.html.haml72
-rw-r--r--app/views/projects/_issuable_form.html.haml29
-rw-r--r--app/views/projects/_issues_nav.html.haml27
-rw-r--r--app/views/projects/_md_preview.html.haml13
-rw-r--r--app/views/projects/branches/index.html.haml4
-rw-r--r--app/views/projects/create.js.haml13
-rw-r--r--app/views/projects/import.html.haml31
-rw-r--r--app/views/projects/imports/new.html.haml21
-rw-r--r--app/views/projects/imports/show.html.haml9
-rw-r--r--app/views/projects/issues/_issue.html.haml4
-rw-r--r--app/views/projects/issues/_issues.html.haml50
-rw-r--r--app/views/projects/issues/index.atom.builder13
-rw-r--r--app/views/projects/issues/show.html.haml4
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml6
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml13
-rw-r--r--app/views/projects/merge_requests/index.html.haml54
-rw-r--r--app/views/projects/merge_requests/show/_mr_box.html.haml4
-rw-r--r--app/views/projects/merge_requests/show/_state_widget.html.haml13
-rw-r--r--app/views/projects/milestones/_form.html.haml11
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/no_repo.html.haml22
-rw-r--r--app/views/projects/notes/_form.html.haml24
-rw-r--r--app/views/projects/notes/_note.html.haml5
-rw-r--r--app/views/search/_project_filter.html.haml1
-rw-r--r--app/views/shared/_choose_group_avatar_button.html.haml2
-rw-r--r--app/views/shared/_promo.html.haml2
-rw-r--r--app/views/shared/_sort_dropdown.html.haml8
-rw-r--r--app/views/users/show.atom.builder12
-rw-r--r--app/views/users/show.html.haml10
-rw-r--r--app/workers/project_service_worker.rb9
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--config/initializers/4_sidekiq.rb3
-rw-r--r--config/routes.rb15
-rw-r--r--config/unicorn.rb.example14
-rw-r--r--db/migrate/20141121161704_add_identity_table.rb38
-rw-r--r--db/migrate/20141205134006_add_locked_at_to_merge_request.rb5
-rw-r--r--db/schema.rb16
-rw-r--r--doc/README.md14
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/architecture.md32
-rw-r--r--doc/development/rake_tasks.md6
-rw-r--r--doc/development/shell_commands.md63
-rw-r--r--doc/development/sidekiq_debugging.md14
-rw-r--r--doc/install/installation.md8
-rw-r--r--doc/install/requirements.md29
-rw-r--r--doc/integration/gitlab_buttons_in_gmail.md17
-rw-r--r--doc/integration/twitter.md2
-rw-r--r--doc/permissions/permissions.md3
-rw-r--r--doc/raketasks/backup_restore.md30
-rw-r--r--doc/release/monthly.md77
-rw-r--r--doc/release/patch.md4
-rw-r--r--doc/release/security.md4
-rw-r--r--doc/update/7.3-to-7.4.md4
-rw-r--r--doc/update/7.4-to-7.5.md93
-rw-r--r--doc/update/7.5-to-7.6.md116
-rw-r--r--doc/update/upgrader.md2
-rw-r--r--doc/web_hooks/web_hooks.md23
-rw-r--r--docker/Dockerfile34
-rw-r--r--docker/README.md68
-rw-r--r--docker/gitlab.rb37
-rw-r--r--docker/troubleshooting.md63
-rw-r--r--features/project/active_tab.feature2
-rw-r--r--features/project/commits/comments.feature8
-rw-r--r--features/project/commits/diff_comments.feature6
-rw-r--r--features/project/issues/issues.feature34
-rw-r--r--features/project/merge_requests.feature31
-rw-r--r--features/project/service.feature6
-rw-r--r--features/steps/profile/profile.rb2
-rw-r--r--features/steps/project/active_tab.rb4
-rw-r--r--features/steps/project/issues/issues.rb1
-rw-r--r--features/steps/project/issues/milestones.rb4
-rw-r--r--features/steps/project/merge_requests.rb1
-rw-r--r--features/steps/project/services.rb15
-rw-r--r--features/steps/shared/diff_note.rb26
-rw-r--r--features/steps/shared/issuable.rb15
-rw-r--r--features/steps/shared/markdown.rb45
-rw-r--r--features/steps/shared/note.rb26
-rw-r--r--features/steps/shared/project.rb2
-rw-r--r--lib/api/entities.rb8
-rw-r--r--lib/api/internal.rb15
-rw-r--r--lib/api/users.rb12
-rw-r--r--lib/backup/database.rb12
-rw-r--r--lib/backup/manager.rb32
-rw-r--r--lib/backup/repository.rb47
-rw-r--r--lib/gitlab/backend/grack_auth.rb2
-rw-r--r--lib/gitlab/force_push_check.rb15
-rw-r--r--lib/gitlab/git_access.rb91
-rw-r--r--lib/gitlab/git_access_status.rb15
-rw-r--r--lib/gitlab/git_access_wiki.rb8
-rw-r--r--lib/gitlab/ldap/access.rb8
-rw-r--r--lib/gitlab/ldap/user.rb13
-rw-r--r--lib/gitlab/oauth/user.rb24
-rw-r--r--lib/gitlab/sidekiq_middleware/memory_killer.rb53
-rw-r--r--lib/tasks/gitlab/backup.rake37
-rw-r--r--lib/tasks/gitlab/mail_google_schema_whitelisting.rake73
-rw-r--r--lib/tasks/gitlab/shell.rake23
-rw-r--r--spec/factories.rb26
-rw-r--r--spec/factories/projects.rb18
-rw-r--r--spec/features/atom/users_spec.rb43
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb23
-rw-r--r--spec/finders/issues_finder_spec.rb81
-rw-r--r--spec/helpers/application_helper_spec.rb10
-rw-r--r--spec/helpers/oauth_helper_spec.rb20
-rw-r--r--spec/lib/gitlab/git_access_spec.rb43
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb4
-rw-r--r--spec/lib/gitlab/ldap/access_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/authentication_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb8
-rw-r--r--spec/lib/gitlab/oauth/user_spec.rb7
-rw-r--r--spec/mailers/notify_spec.rb4
-rw-r--r--spec/models/user_spec.rb16
-rw-r--r--spec/requests/api/internal_spec.rb40
-rw-r--r--spec/requests/api/users_spec.rb2
-rw-r--r--spec/routing/project_routing_spec.rb27
-rw-r--r--spec/tasks/gitlab/mail_google_schema_whitelisting.rb27
186 files changed, 2421 insertions, 1013 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f321c9c32a1..0ddae406cf6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,19 +1,29 @@
v 7.6.0
- Fork repository to groups
- New rugged version
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
+ - Add CRON=1 backup setting for quiet backups
+ - Fix failing wiki restore
+ - Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable)
+ - Monokai highlighting style now more faithful to original design (Mark Riedesel)
+ - Create project with repository in synchrony
+ - Added ability to create empty repo or import existing one if project does not have repository
+ - Reactivate highlight.js language autodetection
+ - Mobile UI improvements
+ - Change maximum avatar file size from 100KB to 200KB
+ - Strict validation for snippet file names
+ - Enable Markdown preview for issues, merge requests, milestones, and notes (Vinnie Okada)
+ - In the docker directory is a container template based on the Omnibus packages.
+ - Update Sidekiq to version 2.17.8
+ - Add author filter to project issues and merge requests pages
+ - Atom feed for user activity
+ - Support multiple omniauth providers for the same user
+ - Rendering cross reference in issue title and tooltip for merge request
+ - Show username in comments
+ - Possibility to create Milestones or Labels when Issues are disabled
+ - Fix bug with showing gpg signature in tag
+
+v 7.5.2
+ - Don't log Sidekiq arguments by default
v 7.5.0
- API: Add support for Hipchat (Kevin Houdebert)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 71435bc600d..9531b27089b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -37,7 +37,7 @@ Please send a merge request with a tested solution or a merge request with a fai
**[Search the issues](https://gitlab.com/gitlab-org/gitlab-ce/issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format (as the first post):
1. **Summary:** Summarize your issue in one sentence (what goes wrong, what did you expect to happen)
-1. **Steps to reproduce:** How can we reproduce the issue, preferably on the [GitLab development virtual machine with vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) (start your issue with: `vagrant destroy && vagrant up && vagrant ssh`)
+1. **Steps to reproduce:** How can we reproduce the issue
1. **Expected behavior:** Describe your issue in detail
1. **Observed behavior**
1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
@@ -75,20 +75,39 @@ If you can, please submit a merge request with the fix or improvements including
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 submission
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
+1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk.
The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as assisting subscribers with upgrade issues, the release of Enterprise Edition and the upgrade of GitLab Cloud. After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features.
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it.
-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). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the following contribution acceptance criteria.
+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). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the contribution acceptance criteria.
-**Please format your merge request description as follows:**
+## Definition of done
+
+If you contribute to GitLab please know that changes involve more than just code.
+We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html).
+Please ensure you support the feature you contribute through all of these steps.
+
+1. Description explaning the relevancy (see following item)
+1. Working and clean code that is commented where needed
+1. Unit and integration tests that pass on the CI server
+1. Documented in the /doc directory
+1. Changelog entry added
+1. Reviewed and any concerns are addressed
+1. Merged by the project lead
+1. Added to the release blog article
+1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/) if relevant
+1. Community questions answered
+1. Answers to questions radiated (in docs/wiki/etc.)
+
+## Merge request description format
1. What does this MR do?
1. Are there points in the code the reviewer needs to double check?
1. Why was this MR needed?
1. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
-1. Screenshots (If appropriate)
+1. Screenshots (if relevant)
## Contribution acceptance criteria
@@ -123,3 +142,17 @@ For examples of feedback on merge requests please look at already [closed merge
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
+
+## Code of conduct
+As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
+
+Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior can be
+reported by emailing contact@gitlab.com
+
+This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 2bf1c1ccf36..197c4d5c2d7 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.3.1
+2.4.0
diff --git a/Gemfile b/Gemfile
index 2c4274dcf3d..b4ca5969277 100644
--- a/Gemfile
+++ b/Gemfile
@@ -112,7 +112,7 @@ gem "acts-as-taggable-on"
# Background jobs
gem 'slim'
gem 'sinatra', require: nil
-gem 'sidekiq', '2.17.0'
+gem 'sidekiq', '2.17.8'
# HTTP requests
gem "httparty"
@@ -134,7 +134,7 @@ gem "redis-rails"
gem 'tinder', '~> 1.9.2'
# HipChat integration
-gem "hipchat", "~> 0.14.0"
+gem "hipchat", "~> 1.4.0"
# Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 0.4.2"
diff --git a/Gemfile.lock b/Gemfile.lock
index 938ce560620..4bcb1eb0de5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -78,7 +78,7 @@ GEM
coffee-script-source (1.6.3)
colored (1.2)
colorize (0.5.8)
- connection_pool (1.2.0)
+ connection_pool (2.1.0)
coveralls (0.7.0)
multi_json (~> 1.3)
rest-client
@@ -235,8 +235,7 @@ GEM
railties (>= 4.0.1)
hashie (2.1.2)
hike (1.2.3)
- hipchat (0.14.0)
- httparty
+ hipchat (1.4.0)
httparty
html-pipeline (1.11.0)
activesupport (>= 2)
@@ -281,7 +280,7 @@ GEM
addressable (~> 2.3)
letter_opener (1.1.2)
launchy (~> 2.2)
- libv8 (3.16.14.3)
+ libv8 (3.16.14.7)
listen (2.3.1)
celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3)
@@ -403,7 +402,7 @@ GEM
rdoc (3.12.2)
json (~> 1.4)
redcarpet (3.1.2)
- redis (3.0.6)
+ redis (3.1.0)
redis-actionpack (4.0.0)
actionpack (~> 4)
redis-rack (~> 1.5.0)
@@ -411,8 +410,8 @@ GEM
redis-activesupport (4.0.0)
activesupport (~> 4)
redis-store (~> 1.1.0)
- redis-namespace (1.4.1)
- redis (~> 3.0.4)
+ redis-namespace (1.5.1)
+ redis (~> 3.0, >= 3.0.4)
redis-rack (1.5.0)
rack (~> 1.5)
redis-store (~> 1.1.0)
@@ -471,12 +470,12 @@ GEM
sexp_processor (4.4.0)
shoulda-matchers (2.1.0)
activesupport (>= 3.0.0)
- sidekiq (2.17.0)
- celluloid (>= 0.15.2)
- connection_pool (>= 1.0.0)
+ sidekiq (2.17.8)
+ celluloid (= 0.15.2)
+ connection_pool (~> 2.0)
json
- redis (>= 3.0.4)
- redis-namespace (>= 1.3.1)
+ redis (~> 3.1)
+ redis-namespace (~> 1.3)
simple_oauth (0.1.9)
simplecov (0.9.0)
docile (~> 1.1.0)
@@ -636,7 +635,7 @@ DEPENDENCIES
guard-rspec
guard-spinach
haml-rails
- hipchat (~> 0.14.0)
+ hipchat (~> 1.4.0)
html-pipeline-gitlab (~> 0.1.0)
httparty
jasmine (= 2.0.2)
@@ -685,7 +684,7 @@ DEPENDENCIES
semantic-ui-sass (~> 0.16.1.0)
settingslogic
shoulda-matchers (~> 2.1.0)
- sidekiq (= 2.17.0)
+ sidekiq (= 2.17.8)
simplecov
sinatra
six
diff --git a/PROCESS.md b/PROCESS.md
index 1dd28d6b670..5cc25de05a4 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -104,3 +104,10 @@ This merge request has been closed because a request for more information has no
### Accepting merge requests
Is there a request on [the feature request forum](http://feedback.gitlab.com/forums/176466-general) that is similar to this? If so, can you make a comment with a link to it? Please be aware that new functionality that is not marked [accepting merge/pull requests](http://feedback.gitlab.com/forums/176466-general/status/796455) on the forum might not make it into GitLab. You might be asked to make changes and even after implementing them your feature might still be declined. If you want to reduce the chance of this happening please have a discussion in the forum first.
+
+### Only accepting merge requests with green tests
+
+We can only accept a merge request if all the tests are green. I've just
+restarted the build. When the tests are still not passing after this restart and
+you're sure that is does not have anything to do with your code changes, please
+rebase with master to see if that solves the issue.
diff --git a/README.md b/README.md
index 63fa5e3da86..f303e8e7383 100644
--- a/README.md
+++ b/README.md
@@ -131,4 +131,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
## Is it awesome?
Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua.
-[These people](https://twitter.com/gitlabhq/favorites) seem to like it.
+[These people](https://twitter.com/gitlab/favorites) seem to like it.
diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee
index a0ebfc98ce6..0ca7070dc8b 100644
--- a/app/assets/javascripts/markdown_area.js.coffee
+++ b/app/assets/javascripts/markdown_area.js.coffee
@@ -24,6 +24,51 @@ $(document).ready ->
"opacity": 0
"display": "none"
+ # Preview button
+ $(document).off "click", ".js-md-preview-button"
+ $(document).on "click", ".js-md-preview-button", (e) ->
+ ###
+ Shows the Markdown preview.
+
+ Lets the server render GFM into Html and displays it.
+ ###
+ e.preventDefault()
+ form = $(this).closest("form")
+ # toggle tabs
+ form.find(".js-md-write-button").parent().removeClass "active"
+ form.find(".js-md-preview-button").parent().addClass "active"
+
+ # toggle content
+ form.find(".md-write-holder").hide()
+ form.find(".md-preview-holder").show()
+
+ preview = form.find(".js-md-preview")
+ mdText = form.find(".markdown-area").val()
+ if mdText.trim().length is 0
+ preview.text "Nothing to preview."
+ else
+ preview.text "Loading..."
+ $.get($(this).data("url"),
+ md_text: mdText
+ ).success (previewData) ->
+ preview.html previewData
+
+ # Write button
+ $(document).off "click", ".js-md-write-button"
+ $(document).on "click", ".js-md-write-button", (e) ->
+ ###
+ Shows the Markdown textarea.
+ ###
+ e.preventDefault()
+ form = $(this).closest("form")
+ # toggle tabs
+ form.find(".js-md-write-button").parent().addClass "active"
+ form.find(".js-md-preview-button").parent().removeClass "active"
+
+ # toggle content
+ form.find(".md-write-holder").show()
+ form.find(".md-preview-holder").hide()
+
dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 978f83dd442..30f8530dfda 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -36,12 +36,6 @@ class @Notes
# delete note attachment
$(document).on "click", ".js-note-attachment-delete", @removeAttachment
- # Preview button
- $(document).on "click", ".js-note-preview-button", @previewNote
-
- # Preview button
- $(document).on "click", ".js-note-write-button", @writeNote
-
# reset main target form after submit
$(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm
@@ -77,8 +71,6 @@ class @Notes
$(document).off "click", ".note-edit-cancel"
$(document).off "click", ".js-note-delete"
$(document).off "click", ".js-note-attachment-delete"
- $(document).off "click", ".js-note-preview-button"
- $(document).off "click", ".js-note-write-button"
$(document).off "ajax:complete", ".js-main-target-form"
$(document).off "click", ".js-choose-note-attachment-button"
$(document).off "click", ".js-discussion-reply-button"
@@ -166,47 +158,6 @@ class @Notes
@removeDiscussionNoteForm(form)
###
- Shows write note textarea.
- ###
- writeNote: (e) ->
- e.preventDefault()
- form = $(this).closest("form")
- # toggle tabs
- form.find(".js-note-write-button").parent().addClass "active"
- form.find(".js-note-preview-button").parent().removeClass "active"
-
- # toggle content
- form.find(".note-write-holder").show()
- form.find(".note-preview-holder").hide()
-
- ###
- Shows the note preview.
-
- Lets the server render GFM into Html and displays it.
- ###
- previewNote: (e) ->
- e.preventDefault()
- form = $(this).closest("form")
- # toggle tabs
- form.find(".js-note-write-button").parent().removeClass "active"
- form.find(".js-note-preview-button").parent().addClass "active"
-
- # toggle content
- form.find(".note-write-holder").hide()
- form.find(".note-preview-holder").show()
-
- preview = form.find(".js-note-preview")
- noteText = form.find(".js-note-text").val()
- if noteText.trim().length is 0
- preview.text "Nothing to preview."
- else
- preview.text "Loading..."
- $.post($(this).data("url"),
- note: noteText
- ).success (previewData) ->
- preview.html previewData
-
- ###
Called in response the main target form has been successfully submitted.
Removes any errors.
@@ -220,7 +171,7 @@ class @Notes
form.find(".js-errors").remove()
# reset text and preview
- form.find(".js-note-write-button").click()
+ form.find(".js-md-write-button").click()
form.find(".js-note-text").val("").trigger "input"
###
@@ -270,8 +221,8 @@ class @Notes
form.removeClass "js-new-note-form"
# setup preview buttons
- form.find(".js-note-write-button, .js-note-preview-button").tooltip placement: "left"
- previewButton = form.find(".js-note-preview-button")
+ form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left"
+ previewButton = form.find(".js-md-preview-button")
form.find(".js-note-text").on "input", ->
if $(this).val().trim() isnt ""
previewButton.removeClass("turn-off").addClass "turn-on"
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index cd2f4e45e3c..2fc738c18d8 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -330,10 +330,6 @@ table {
}
}
-@media (max-width: $screen-xs-max) {
- .container .content { margin-top: 20px; }
-}
-
.wiki .highlight, .note-body .highlight {
margin-bottom: 9px;
}
diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/generic/highlight.scss
index 4110bddf4f3..ae08539d454 100644
--- a/app/assets/stylesheets/generic/highlight.scss
+++ b/app/assets/stylesheets/generic/highlight.scss
@@ -59,6 +59,10 @@
pre {
white-space: pre;
word-wrap: normal;
+
+ code {
+ font-family: $monospace_font;
+ }
}
}
}
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
index 94149594e24..79fbad4b946 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -113,6 +113,11 @@
padding: 10px 15px;
}
+ .cross-project-ref {
+ float: left;
+ padding: 10px 15px;
+ }
+
.creator {
float: right;
padding: 10px 15px;
diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss
index fbfa72c5e5e..4168e235cae 100644
--- a/app/assets/stylesheets/generic/markdown_area.scss
+++ b/app/assets/stylesheets/generic/markdown_area.scss
@@ -20,6 +20,7 @@
opacity: 0;
font-size: 50px;
transition: opacity 200ms ease-in-out;
+ pointer-events: none;
}
.div-dropzone-spinner {
@@ -50,3 +51,28 @@
margin-bottom: 0;
transition: opacity 200ms ease-in-out;
}
+
+.md-preview-holder {
+ background: #FFF;
+ border: 1px solid #ddd;
+ min-height: 100px;
+ padding: 5px;
+ font-size: 14px;
+ box-shadow: none;
+}
+
+.new_note,
+.edit_note,
+.issuable-description,
+.milestone-description,
+.merge-request-form {
+ .nav-tabs {
+ margin-bottom: 0;
+ border: none;
+
+ li a,
+ li.active a {
+ border: 1px solid #DDD;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss
new file mode 100644
index 00000000000..c164b07b104
--- /dev/null
+++ b/app/assets/stylesheets/generic/mobile.scss
@@ -0,0 +1,17 @@
+/** Common mobile (screen XS) styles **/
+@media (max-width: $screen-xs-max) {
+ .container .content {
+ margin-top: 20px;
+ }
+
+ .nav.nav-tabs > li > a {
+ padding: 10px;
+ font-size: 12px;
+ margin-right: 3px;
+
+ .badge {
+ display: none;
+ }
+ }
+}
+
diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss
index f29cf25fa4c..57e9e8ae5c5 100644
--- a/app/assets/stylesheets/generic/timeline.scss
+++ b/app/assets/stylesheets/generic/timeline.scss
@@ -75,3 +75,20 @@
}
}
}
+
+@media (max-width: $screen-xs-max) {
+ .timeline {
+ &:before {
+ background: none;
+ }
+ .timeline-entry .timeline-entry-inner {
+ .timeline-icon {
+ display: none;
+ }
+
+ .timeline-content {
+ margin-left: 0;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 36bc5df2f44..dffa2dc9ed2 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -29,28 +29,30 @@
.hljs-tag,
.hljs-tag .hljs-title,
- .hljs-keyword,
- .hljs-literal,
.hljs-strong,
.hljs-change,
.hljs-winutils,
.hljs-flow,
.lisp .hljs-title,
.clojure .hljs-built_in,
+ .hljs-keyword,
.nginx .hljs-title,
.tex .hljs-special {
color: #F92672;
}
.hljs {
- color: #DDD;
+ color: #F8F8F2;
}
- .hljs .hljs-constant,
- .asciidoc .hljs-code {
+ .asciidoc .hljs-code,
+ .markdown .hljs-code,
+ .hljs-literal,
+ .hljs-function .hljs-keyword {
color: #66D9EF;
}
+
.hljs-code,
.hljs-class .hljs-title,
.hljs-header {
@@ -62,18 +64,27 @@
.hljs-symbol,
.hljs-symbol .hljs-string,
.hljs-value,
+ .hljs-constant,
+ .hljs-number,
.hljs-regexp {
- color: #BF79DB;
+ color: #AE81FF;
+ }
+
+ .hljs-string {
+ color: #E6DB74;
+ }
+
+ .hljs-params {
+ color: #fd971f;
}
.hljs-link_url,
.hljs-tag .hljs-value,
- .hljs-string,
.hljs-bullet,
.hljs-subst,
.hljs-title,
.hljs-emphasis,
- .haskell .hljs-type,
+ .hljs-type,
.hljs-preprocessor,
.hljs-pragma,
.ruby .hljs-class .hljs-parent,
@@ -99,12 +110,12 @@
}
.hljs-comment,
- .java .hljs-annotation,
+ .hljs-annotation,
.smartquote,
.hljs-blockquote,
.hljs-horizontal_rule,
- .python .hljs-decorator,
.hljs-template_comment,
+ .hljs-decorator,
.hljs-pi,
.hljs-doctype,
.hljs-deletion,
diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss
index 485a9c46610..a766d6e77ab 100644
--- a/app/assets/stylesheets/sections/events.scss
+++ b/app/assets/stylesheets/sections/events.scss
@@ -47,7 +47,7 @@
.event-title {
@include str-truncated(72%);
color: #333;
- font-weight: normal;
+ font-weight: 500;
font-size: 14px;
.author_name {
color: #333;
@@ -56,12 +56,9 @@
.event-body {
margin-left: 35px;
margin-right: 100px;
+ color: #777;
- .event-info {
- color: #666;
- }
.event-note {
- color: #666;
margin-top: 5px;
.md {
@@ -72,7 +69,7 @@
border: none;
background: #f9f9f9;
border-radius: 0;
- color: #666;
+ color: #777;
margin: 0 20px;
}
@@ -120,7 +117,6 @@
padding: 3px;
padding-left: 0;
border: none;
- color: #666;
.commit-row-title {
font-size: 12px;
}
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index ebf8a6125c7..9a5400fffbc 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -151,4 +151,14 @@ form.edit-issue {
}
}
}
+
+ .issue {
+ &:hover .issue-actions {
+ display: none !important;
+ }
+
+ .issue-updated-at {
+ display: none;
+ }
+ }
}
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index 7eb42fddade..e1f9c0cb258 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -36,13 +36,16 @@ ul.notes {
font-size: 13px;
}
.author {
- color: #555;
+ color: #333;
font-weight: bold;
font-size: 14px;
&:hover {
- color: $link_hover_color;
+ color: $link_color;
}
}
+ .author-username {
+ font-size: 14px;
+ }
}
.discussion {
@@ -224,7 +227,6 @@ ul.notes {
margin-bottom: 0;
}
- .note-preview-holder,
.note_text {
background: #FFF;
border: 1px solid #ddd;
@@ -243,15 +245,6 @@ ul.notes {
.note_text {
width: 100%;
}
- .nav-tabs {
- margin-bottom: 0;
- border: none;
-
- li a,
- li.active a {
- border: 1px solid #DDD;
- }
- }
}
/* loading indicator */
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index bd4b310fcbf..3e984e5007a 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -42,10 +42,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def handle_omniauth
if current_user
- # Change a logged-in user's authentication method:
- current_user.extern_uid = oauth['uid']
- current_user.provider = oauth['provider']
- current_user.save
+ # Add new authentication method
+ current_user.identities.find_or_create_by(extern_uid: oauth['uid'], provider: oauth['provider'])
redirect_to profile_path
else
@user = Gitlab::OAuth::User.new(oauth)
@@ -67,8 +65,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
end
end
- rescue StandardError
- flash[:notice] = "There's no such user!"
+ rescue ForbiddenAction => e
+ flash[:notice] = e.message
redirect_to new_user_session_path
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 7e4580017dd..6b7fe06d59f 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -29,4 +29,31 @@ class Projects::ApplicationController < ApplicationController
redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
end
end
+
+ def set_filter_variables(collection)
+ params[:sort] ||= 'newest'
+ params[:scope] = 'all' if params[:scope].blank?
+ params[:state] = 'opened' if params[:state].blank?
+
+ @sort = params[:sort].humanize
+
+ assignee_id = params[:assignee_id]
+ author_id = params[:author_id]
+ milestone_id = params[:milestone_id]
+
+ if assignee_id.present? && !assignee_id.to_i.zero?
+ @assignee = @project.team.find(assignee_id)
+ end
+
+ if author_id.present? && !author_id.to_i.zero?
+ @author = @project.team.find(assignee_id)
+ end
+
+ if milestone_id.present? && !milestone_id.to_i.zero?
+ @milestone = @project.milestones.find(milestone_id)
+ end
+
+ @assignees = User.where(id: collection.pluck(:assignee_id))
+ @authors = User.where(id: collection.pluck(:author_id))
+ end
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index cab8fd76e6c..2d6c3111192 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -26,6 +26,7 @@ class Projects::HooksController < Projects::ApplicationController
def test
if !@project.empty_repo?
status = TestHookService.new.execute(hook, current_user)
+
if status
flash[:notice] = 'Hook successfully executed.'
else
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
new file mode 100644
index 00000000000..b8350642804
--- /dev/null
+++ b/app/controllers/projects/imports_controller.rb
@@ -0,0 +1,49 @@
+class Projects::ImportsController < Projects::ApplicationController
+ # Authorize
+ before_filter :authorize_admin_project!
+ before_filter :require_no_repo
+ before_filter :redirect_if_progress, except: :show
+
+ def new
+ end
+
+ def create
+ @project.import_url = params[:project][:import_url]
+
+ if @project.save
+ @project.reload
+
+ if @project.import_failed?
+ @project.import_retry
+ else
+ @project.import_start
+ end
+ end
+
+ redirect_to project_import_path(@project)
+ end
+
+ def show
+ unless @project.import_in_progress?
+ if @project.import_finished?
+ redirect_to(@project) and return
+ else
+ redirect_to new_project_import_path(@project) and return
+ end
+ end
+ end
+
+ private
+
+ def require_no_repo
+ if @project.repository_exists?
+ redirect_to(@project) and return
+ end
+ end
+
+ def redirect_if_progress
+ if @project.import_in_progress?
+ redirect_to project_import_path(@project) and return
+ end
+ end
+end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index c6d526f05c5..22235123826 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -18,18 +18,12 @@ class Projects::IssuesController < Projects::ApplicationController
def index
terms = params['issue_search']
+ set_filter_variables(@project.issues)
- @issues = issues_filtered
+ @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
@issues = @issues.full_search(terms) if terms.present?
@issues = @issues.page(params[:page]).per(20)
- assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
- @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
- @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
- sort_param = params[:sort] || 'newest'
- @sort = sort_param.humanize unless sort_param.empty?
- @assignees = User.where(id: @project.issues.pluck(:assignee_id)).active
-
respond_to do |format|
format.html
format.atom { render layout: false }
@@ -127,12 +121,6 @@ class Projects::IssuesController < Projects::ApplicationController
return render_404 unless @project.issues_enabled
end
- def issues_filtered
- params[:scope] = 'all' if params[:scope].blank?
- params[:state] = 'opened' if params[:state].blank?
- @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
- end
-
# Since iids are implemented only in 6.1
# user may navigate to issue page using old global ids.
#
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 20a733b10e1..4d6f41e9de5 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -17,18 +17,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
def index
- params[:sort] ||= 'newest'
- params[:scope] = 'all' if params[:scope].blank?
- params[:state] = 'opened' if params[:state].blank?
+ set_filter_variables(@project.merge_requests)
@merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
@merge_requests = @merge_requests.page(params[:page]).per(20)
-
- @sort = params[:sort].humanize
- assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
- @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
- @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
- @assignees = User.where(id: @project.merge_requests.pluck(:assignee_id))
end
def show
@@ -225,6 +217,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@allowed_to_merge = allowed_to_merge?
@show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge
@source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name)
+
+ if @merge_request.locked_long_ago?
+ @merge_request.unlock_mr
+ @merge_request.close
+ end
end
def allowed_to_merge?
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 7b08b79d236..2f1d631c14a 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -61,10 +61,6 @@ class Projects::NotesController < Projects::ApplicationController
end
end
- def preview
- render text: view_context.markdown(params[:note])
- end
-
private
def note
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index bcd14a1c847..3a90c1c806d 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -1,7 +1,14 @@
class Projects::RepositoriesController < Projects::ApplicationController
# Authorize
before_filter :authorize_download_code!
- before_filter :require_non_empty_project
+ before_filter :require_non_empty_project, except: :create
+ before_filter :authorize_admin_project!, only: :create
+
+ def create
+ @project.create_repository
+
+ redirect_to @project
+ end
def archive
unless can?(current_user, :download_code, @project)
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index a5f30dcfd9d..c50a1f1e75b 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -42,7 +42,7 @@ class Projects::ServicesController < Projects::ApplicationController
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
- :build_key
+ :build_key, :server
)
end
end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 9d5dd8a95cc..25c887deafa 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -68,7 +68,7 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet.content,
type: 'text/plain; charset=utf-8',
disposition: 'inline',
- filename: @snippet.file_name
+ filename: @snippet.sanitized_file_name
)
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index b3181fa310e..fcff6952d38 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -4,7 +4,7 @@ class ProjectsController < ApplicationController
before_filter :repository, except: [:new, :create]
# Authorize
- before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import]
+ before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
layout 'navless', only: [:new, :create, :fork]
before_filter :set_title, only: [:new, :create]
@@ -19,10 +19,11 @@ class ProjectsController < ApplicationController
def create
@project = ::Projects::CreateService.new(current_user, project_params).execute
- flash[:notice] = 'Project was successfully created.' if @project.saved?
- respond_to do |format|
- format.js
+ if @project.saved?
+ redirect_to project_path(@project), notice: 'Project was successfully created.'
+ else
+ render 'new'
end
end
@@ -47,7 +48,7 @@ class ProjectsController < ApplicationController
def show
if @project.import_in_progress?
- redirect_to import_project_path(@project)
+ redirect_to project_import_path(@project)
return
end
@@ -60,37 +61,20 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html do
- if @project.empty_repo?
- render "projects/empty", layout: user_layout
+ if @project.repository_exists?
+ if @project.empty_repo?
+ render "projects/empty", layout: user_layout
+ else
+ @last_push = current_user.recent_push(@project.id) if current_user
+ render :show, layout: user_layout
+ end
else
- @last_push = current_user.recent_push(@project.id) if current_user
- render :show, layout: user_layout
+ render "projects/no_repo", layout: user_layout
end
end
- format.json { pager_json("events/_events", @events.count) }
- 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 = project_params[:import_url]
-
- if @project.save
- @project.reload
- @project.import_retry
+ format.json { pager_json("events/_events", @events.count) }
end
-
- redirect_to import_project_path(@project)
end
def destroy
@@ -163,6 +147,10 @@ class ProjectsController < ApplicationController
render json: { star_count: @project.star_count }
end
+ def markdown_preview
+ render text: view_context.markdown(params[:md_text])
+ end
+
private
def upload_path
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index bf3312fedc8..312e561b522 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -79,7 +79,7 @@ class SnippetsController < ApplicationController
@snippet.content,
type: 'text/plain; charset=utf-8',
disposition: 'inline',
- filename: @snippet.file_name
+ filename: @snippet.sanitized_file_name
)
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 0b442f5383a..67af1801bda 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -20,9 +20,14 @@ class UsersController < ApplicationController
# Get user activity feed for projects common for both users
@events = @user.recent_events.
- where(project_id: authorized_projects_ids).limit(20)
+ where(project_id: authorized_projects_ids).limit(30)
@title = @user.name
+
+ respond_to do |format|
+ format.html
+ format.atom { render layout: false }
+ end
end
def determine_layout
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index d0574240511..e1477510065 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -33,6 +33,7 @@ class IssuableFinder
items = by_search(items)
items = by_milestone(items)
items = by_assignee(items)
+ items = by_author(items)
items = by_label(items)
items = sort(items)
end
@@ -125,6 +126,14 @@ class IssuableFinder
items
end
+ def by_author(items)
+ if params[:author_id].present?
+ items = items.where(author_id: (params[:author_id] == '0' ? nil : params[:author_id]))
+ end
+
+ items
+ end
+
def by_label(items)
if params[:label_name].present?
label_names = params[:label_name].split(",")
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 11fbf1baae7..420ac3f77c7 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,14 +1,9 @@
module BlobHelper
def highlightjs_class(blob_name)
- if blob_name.include?('.')
- ext = blob_name.split('.').last
- return 'language-' + ext
+ if no_highlight_files.include?(blob_name.downcase)
+ 'no-highlight'
else
- if no_highlight_files.include?(blob_name.downcase)
- 'no-highlight'
- else
- blob_name.downcase
- end
+ blob_name.downcase
end
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 71f97fbb8c8..a3136926b38 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -145,4 +145,26 @@ module EventsHelper
rescue
"--broken encoding"
end
+
+ def event_to_atom(xml, event)
+ if event.proper?
+ xml.entry do
+ event_link = event_feed_url(event)
+ event_title = event_feed_title(event)
+ event_summary = event_feed_summary(event)
+
+ xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
+ xml.link href: event_link
+ xml.title truncate(event_title, length: 80)
+ xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
+ xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email)
+ xml.author do |author|
+ xml.name event.author_name
+ xml.email event.author_email
+ end
+
+ xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? }
+ end
+ end
+ end
end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 7d3cb749829..800cacdc2c2 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -254,4 +254,16 @@ module GitlabMarkdownHelper
truncated
end
end
+
+ def cross_project_reference(project, entity)
+ path = project.path_with_namespace
+
+ if entity.kind_of?(Issue)
+ [path, entity.iid].join('#')
+ elsif entity.kind_of?(MergeRequest)
+ [path, entity.iid].join('!')
+ else
+ raise 'Not supported type'
+ end
+ end
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index d513e0ba58e..a5b393c1e3c 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -113,4 +113,19 @@ module IssuesHelper
'issue-box-open'
end
end
+
+ def issue_to_atom(xml, issue)
+ xml.entry do
+ xml.id project_issue_url(issue.project, issue)
+ xml.link href: project_issue_url(issue.project, issue)
+ xml.title truncate(issue.title, length: 80)
+ xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
+ xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email)
+ xml.author do |author|
+ xml.name issue.author_name
+ xml.email issue.author_email
+ end
+ xml.summary issue.title
+ end
+ end
end
diff --git a/app/helpers/oauth_helper.rb b/app/helpers/oauth_helper.rb
index 7024483b8b3..df18db71c84 100644
--- a/app/helpers/oauth_helper.rb
+++ b/app/helpers/oauth_helper.rb
@@ -16,4 +16,8 @@ module OauthHelper
[:twitter, :github, :google_oauth2].include?(name.to_sym)
end
end
+
+ def additional_providers
+ enabled_oauth_providers.reject{|provider| provider.to_s.starts_with?('ldap')}
+ end
end
diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb
index 0b375558305..6480fd3886f 100644
--- a/app/helpers/profile_helper.rb
+++ b/app/helpers/profile_helper.rb
@@ -1,6 +1,6 @@
module ProfileHelper
def oauth_active_class(provider)
- if current_user.provider == provider.to_s
+ if current_user.identities.exists?(provider: provider.to_s)
'active'
end
end
@@ -10,10 +10,10 @@ module ProfileHelper
end
def show_profile_social_tab?
- enabled_social_providers.any? && !current_user.ldap_user?
+ enabled_social_providers.any?
end
def show_profile_remove_tab?
- gitlab_config.signup_enabled && !current_user.ldap_user?
+ gitlab_config.signup_enabled
end
end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
new file mode 100644
index 00000000000..492e065b713
--- /dev/null
+++ b/app/helpers/sorting_helper.rb
@@ -0,0 +1,17 @@
+module SortingHelper
+ def sort_title_oldest_updated
+ 'Oldest updated'
+ end
+
+ def sort_title_recently_updated
+ 'Recently updated'
+ end
+
+ def sort_title_oldest_created
+ 'Oldest created'
+ end
+
+ def sort_title_recently_created
+ 'Recently created'
+ end
+end
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index f8a7d133d1d..6d7f8eb4b02 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -1,8 +1,7 @@
module Emails
module Profile
- def new_user_email(user_id, password, token = nil)
+ def new_user_email(user_id, token = nil)
@user = User.find(user_id)
- @password = password
@target_url = user_url(@user)
@token = token
mail(to: @user.email, subject: subject("Account was created for you"))
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 0ee19836627..6d671e6e0bd 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -26,6 +26,14 @@ class Notify < ActionMailer::Base
delay_for(2.seconds)
end
+ def test_email(recepient_email, subject, body)
+ mail(to: recepient_email,
+ subject: subject,
+ body: body.html_safe,
+ content_type: 'text/html'
+ )
+ end
+
private
# The default email address to send emails from
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 212229649fc..37dd371ec00 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -10,12 +10,12 @@ class Commit
# Used to prevent 500 error on huge commits by suppressing diff
#
# User can force display of diff above this size
- DIFF_SAFE_FILES = 100
- DIFF_SAFE_LINES = 5000
+ DIFF_SAFE_FILES = 100 unless defined?(DIFF_SAFE_FILES)
+ DIFF_SAFE_LINES = 5000 unless defined?(DIFF_SAFE_LINES)
# Commits above this size will not be rendered in HTML
- DIFF_HARD_LIMIT_FILES = 1000
- DIFF_HARD_LIMIT_LINES = 50000
+ DIFF_HARD_LIMIT_FILES = 1000 unless defined?(DIFF_HARD_LIMIT_FILES)
+ DIFF_HARD_LIMIT_LINES = 50000 unless defined?(DIFF_HARD_LIMIT_LINES)
class << self
def decorate(commits)
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 23fa01e0b70..8479d4aecf6 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -32,7 +32,10 @@ class WebHook < ActiveRecord::Base
def execute(data)
parsed_url = URI.parse(url)
if parsed_url.userinfo.blank?
- WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false)
+ WebHook.post(url,
+ body: data.to_json,
+ headers: { "Content-Type" => "application/json" },
+ verify: false)
else
post_url = url.gsub("#{parsed_url.userinfo}@", "")
auth = {
@@ -45,6 +48,9 @@ class WebHook < ActiveRecord::Base
verify: false,
basic_auth: auth)
end
+ rescue SocketError, Errno::ECONNREFUSED => e
+ logger.error("WebHook Error => #{e}")
+ false
end
def async_execute(data)
diff --git a/app/models/identity.rb b/app/models/identity.rb
new file mode 100644
index 00000000000..5fb1850c30b
--- /dev/null
+++ b/app/models/identity.rb
@@ -0,0 +1,5 @@
+class Identity < ActiveRecord::Base
+ belongs_to :user
+
+ validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
+end \ No newline at end of file
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 7c525b02f48..2cc427d35c2 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -70,6 +70,16 @@ class MergeRequest < ActiveRecord::Base
transition locked: :reopened
end
+ after_transition any => :locked do |merge_request, transition|
+ merge_request.locked_at = Time.now
+ merge_request.save
+ end
+
+ after_transition :locked => (any - :locked) do |merge_request, transition|
+ merge_request.locked_at = nil
+ merge_request.save
+ end
+
state :opened
state :reopened
state :closed
@@ -336,4 +346,8 @@ class MergeRequest < ActiveRecord::Base
source_project.repository.branch_names
end
end
+
+ def locked_long_ago?
+ locked_at && locked_at < (Time.now - 1.day)
+ end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 5bf645bbd1d..5996298be22 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -502,6 +502,6 @@ class Note < ActiveRecord::Base
end
def editable?
- !system
+ !read_attribute(:system)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index d2576bb85d0..32b0145ca24 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -136,7 +136,7 @@ class Project < ActiveRecord::Base
state_machine :import_status, initial: :none do
event :import_start do
- transition :none => :started
+ transition [:none, :finished] => :started
end
event :import_finish do
@@ -390,14 +390,8 @@ class Project < ActiveRecord::Base
end
def execute_services(data)
- services.each do |service|
-
- # Call service hook only if it is active
- begin
- service.execute(data) if service.active
- rescue => e
- logger.error(e)
- end
+ services.select(&:active).each do |service|
+ service.async_execute(data)
end
end
@@ -586,4 +580,25 @@ class Project < ActiveRecord::Base
def origin_merge_requests
merge_requests.where(source_project_id: self.id)
end
+
+ def create_repository
+ if gitlab_shell.add_repository(path_with_namespace)
+ true
+ else
+ errors.add(:base, "Failed to create repository")
+ false
+ end
+ end
+
+ def repository_exists?
+ !!repository.exists?
+ end
+
+ def create_wiki
+ ProjectWiki.new(self, self.owner).wiki
+ true
+ rescue ProjectWiki::CouldNotCreateWikiError => ex
+ errors.add(:base, "Failed create wiki")
+ false
+ end
end
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 4078938cdbb..a848d74044c 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -15,11 +15,11 @@
class HipchatService < Service
MAX_COMMITS = 3
- prop_accessor :token, :room
+ prop_accessor :token, :room, :server
validates :token, presence: true, if: :activated?
def title
- 'Hipchat'
+ 'HipChat'
end
def description
@@ -33,7 +33,9 @@ class HipchatService < Service
def fields
[
{ type: 'text', name: 'token', placeholder: '' },
- { type: 'text', name: 'room', placeholder: '' }
+ { type: 'text', name: 'room', placeholder: '' },
+ { type: 'text', name: 'server',
+ placeholder: 'Leave blank for default. https://chat.hipchat.com' }
]
end
@@ -44,7 +46,9 @@ class HipchatService < Service
private
def gate
- @gate ||= HipChat::Client.new(token)
+ options = { api_version: 'v2' }
+ options[:server_url] = server unless server.nil?
+ @gate ||= HipChat::Client.new(token, options)
end
def create_message(push)
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 770a26ed894..f8a28ca9866 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -5,7 +5,7 @@ class ProjectWiki
'Markdown' => :markdown,
'RDoc' => :rdoc,
'AsciiDoc' => :asciidoc
- }
+ } unless defined?(MARKUPS)
class CouldNotCreateWikiError < StandardError; end
diff --git a/app/models/service.rb b/app/models/service.rb
index c489c1e96e1..71c8aa39e45 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -82,4 +82,8 @@ class Service < ActiveRecord::Base
}
end
end
+
+ def async_execute(data)
+ Sidekiq::Client.enqueue(ProjectServiceWorker, id, data)
+ end
end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index a47fbca3260..9aba42a0622 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -29,7 +29,9 @@ class Snippet < ActiveRecord::Base
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
- validates :file_name, presence: true, length: { within: 0..255 }
+ validates :file_name, presence: true, length: { within: 0..255 },
+ format: { with: Gitlab::Regex.path_regex,
+ message: Gitlab::Regex.path_regex_message }
validates :content, presence: true
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
@@ -62,6 +64,10 @@ class Snippet < ActiveRecord::Base
file_name
end
+ def sanitized_file_name
+ file_name.gsub(/[^a-zA-Z0-9_\-\.]+/, '')
+ end
+
def mode
nil
end
@@ -72,7 +78,7 @@ class Snippet < ActiveRecord::Base
def visibility_level_field
visibility_level
- end
+ end
class << self
def search(query)
diff --git a/app/models/user.rb b/app/models/user.rb
index fc191a78f53..7faeef1b5b0 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -79,6 +79,7 @@ class User < ActiveRecord::Base
# Profile
has_many :keys, dependent: :destroy
has_many :emails, dependent: :destroy
+ has_many :identities, dependent: :destroy
# Groups
has_many :members, dependent: :destroy
@@ -113,7 +114,6 @@ class User < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true, email: {strict_mode: true}, uniqueness: true
validates :bio, length: { maximum: 255 }, allow_blank: true
- validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
validates :username, presence: true, uniqueness: { case_sensitive: false },
exclusion: { in: Gitlab::Blacklist.path },
@@ -124,7 +124,7 @@ class User < ActiveRecord::Base
validate :namespace_uniq, if: ->(user) { user.username_changed? }
validate :avatar_type, if: ->(user) { user.avatar_changed? }
validate :unique_email, if: ->(user) { user.email_changed? }
- validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
+ validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :generate_password, on: :create
before_validation :sanitize_attrs
@@ -178,7 +178,6 @@ class User < ActiveRecord::Base
scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
- scope :ldap, -> { where('provider LIKE ?', 'ldap%') }
scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active }
#
@@ -407,7 +406,11 @@ class User < ActiveRecord::Base
end
def ldap_user?
- extern_uid && provider.start_with?('ldap')
+ identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"])
+ end
+
+ def ldap_identity
+ @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
end
def accessible_deploy_keys
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 74448998ddd..baf0936cc3d 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -3,6 +3,7 @@ module MergeRequests
def execute(oldrev, newrev, ref)
return true unless ref =~ /heads/
+ @oldrev, @newrev = oldrev, newrev
@branch_name = ref.gsub("refs/heads/", "")
@fork_merge_requests = @project.fork_merge_requests.opened
@commits = @project.repository.commits_between(oldrev, newrev)
@@ -35,6 +36,10 @@ module MergeRequests
end
end
+ def force_push?
+ Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev)
+ end
+
# Refresh merge request diff if we push to source or target branch of merge request
# Note: we should update merge requests from forks too
def reload_merge_requests
@@ -43,8 +48,22 @@ module MergeRequests
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
- merge_request.reload_code
- merge_request.mark_as_unchecked
+
+ if merge_request.source_branch == @branch_name || force_push?
+ merge_request.reload_code
+ merge_request.mark_as_unchecked
+ else
+ mr_commit_ids = merge_request.commits.map(&:id)
+ push_commit_ids = @commits.map(&:id)
+ matches = mr_commit_ids & push_commit_ids
+
+ if matches.any?
+ merge_request.reload_code
+ merge_request.mark_as_unchecked
+ else
+ merge_request.mark_as_unchecked
+ end
+ end
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index c9a1574b84e..d1aadd741e1 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -107,7 +107,7 @@ class NotificationService
# Notify new user with email after creation
def new_user(user, token = nil)
# Don't email omniauth created users
- mailer.new_user_email(user.id, user.password, token) unless user.extern_uid?
+ mailer.new_user_email(user.id, token) unless user.identities.any?
end
# Notify users on new note in system
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 12386792aab..3672b623806 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -37,35 +37,22 @@ module Projects
@project.creator = current_user
- if @project.save
- log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
- system_hook_service.execute_hooks_for(@project, :create)
+ Project.transaction do
+ @project.save
- unless @project.group
- @project.team << [current_user, :master]
- 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
- )
+ unless @project.import?
+ unless @project.create_repository
+ raise 'Failed to create repository'
+ end
end
+ end
+ if @project.persisted?
if @project.wiki_enabled?
- begin
- # force the creation of a wiki,
- ProjectWiki.new(@project, @project.owner).wiki
- rescue ProjectWiki::CouldNotCreateWikiError => ex
- # Prevent project observer crash
- # if failed to create wiki
- nil
- end
+ @project.create_wiki
end
+
+ after_create_actions
end
@project
@@ -84,5 +71,20 @@ module Projects
namespace = Namespace.find_by(id: namespace_id)
current_user.can?(:create_projects, namespace)
end
+
+ def after_create_actions
+ log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
+ system_hook_service.execute_hooks_for(@project, :create)
+
+ unless @project.group
+ @project.team << [current_user, :master]
+ end
+
+ @project.update_column(:last_activity_at, @project.created_at)
+
+ if @project.import?
+ @project.import_start
+ end
+ end
end
end
diff --git a/app/services/test_hook_service.rb b/app/services/test_hook_service.rb
index b6b1ef29b51..17d86a7a274 100644
--- a/app/services/test_hook_service.rb
+++ b/app/services/test_hook_service.rb
@@ -2,8 +2,5 @@ class TestHookService
def execute(hook, current_user)
data = GitPushService.new.sample_data(hook.project, current_user)
hook.execute(data)
- true
- rescue SocketError
- false
end
end
diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb
index 29a55b36ca5..b122b6c8658 100644
--- a/app/uploaders/attachment_uploader.rb
+++ b/app/uploaders/attachment_uploader.rb
@@ -26,10 +26,6 @@ class AttachmentUploader < CarrierWave::Uploader::Base
Gitlab.config.gitlab.relative_url_root + "/files/#{model.class.to_s.underscore}/#{model.id}/#{file.filename}"
end
- def url
- Gitlab.config.gitlab.relative_url_root + super unless super.nil?
- end
-
def file_storage?
self.class.storage == CarrierWave::Storage::File
end
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 2cd6b12be7f..aa59f38d213 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -56,13 +56,13 @@
= link_to admin_projects_path(sort: nil) do
Name
= link_to admin_projects_path(sort: 'newest') do
- Newest
+ = sort_title_recently_created
= link_to admin_projects_path(sort: 'oldest') do
- Oldest
+ = sort_title_oldest_created
= link_to admin_projects_path(sort: 'recently_updated') do
- Recently updated
+ = sort_title_recently_updated
= link_to admin_projects_path(sort: 'last_updated') do
- Last updated
+ = sort_title_oldest_updated
= link_to admin_projects_path(sort: 'largest_repository') do
Largest repository
= link_to 'New Project', new_project_path, class: "btn btn-new"
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 92c619738a2..8e1ecb41a85 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -49,9 +49,10 @@
= link_to admin_users_path(sort: 'oldest_sign_in') do
Oldest sign in
= link_to admin_users_path(sort: 'recently_created') do
- Recently created
+ = sort_title_recently_created
= link_to admin_users_path(sort: 'late_created') do
- Late created
+ = sort_title_oldest_created
+
= link_to 'New User', new_admin_user_path, class: "btn btn-new"
%ul.well-list
- @users.each do |user|
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 211d77d5185..29717aedd80 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -95,7 +95,7 @@
%li
%span.light LDAP uid:
%strong
- = @user.extern_uid
+ = @user.ldap_identity.extern_uid
- if @user.created_by
%li
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index f5413557783..66381310221 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -1,24 +1,13 @@
xml.instruct!
-xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
+xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do
xml.title "#{current_user.name} issues"
- xml.link :href => issues_dashboard_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml"
- xml.link :href => issues_dashboard_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html"
- xml.id issues_dashboard_url(:private_token => current_user.private_token)
+ xml.link href: issues_dashboard_url(:atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
+ xml.link href: issues_dashboard_url(private_token: current_user.private_token), rel: "alternate", type: "text/html"
+ xml.id issues_dashboard_url(private_token: current_user.private_token)
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
- xml.entry do
- xml.id project_issue_url(issue.project, issue)
- xml.link :href => project_issue_url(issue.project, issue)
- xml.title truncate(issue.title, :length => 80)
- xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
- xml.author do |author|
- xml.name issue.author_name
- xml.email issue.author_email
- end
- xml.summary issue.title
- end
+ issue_to_atom(xml, issue)
end
end
diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml
index f124c688be1..5b7835b097b 100644
--- a/app/views/dashboard/projects.html.haml
+++ b/app/views/dashboard/projects.html.haml
@@ -14,13 +14,14 @@
= link_to projects_dashboard_filter_path(sort: nil) do
Name
= link_to projects_dashboard_filter_path(sort: 'newest') do
- Newest
+ = sort_title_recently_created
= link_to projects_dashboard_filter_path(sort: 'oldest') do
- Oldest
+ = sort_title_oldest_created
= link_to projects_dashboard_filter_path(sort: 'recently_updated') do
- Recently updated
+ = sort_title_recently_updated
= link_to projects_dashboard_filter_path(sort: 'last_updated') do
- Last updated
+ = sort_title_oldest_updated
+
%p.light
All projects you have access to are listed here. Public projects are not included here unless you are a member
%hr
diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder
index f4cf24ccd99..70ac66f8016 100644
--- a/app/views/dashboard/show.atom.builder
+++ b/app/views/dashboard/show.atom.builder
@@ -1,29 +1,12 @@
xml.instruct!
-xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
+xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do
xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"
- xml.link :href => dashboard_url(:atom), :rel => "self", :type => "application/atom+xml"
- xml.link :href => dashboard_url, :rel => "alternate", :type => "text/html"
+ xml.link href: dashboard_url(:atom), rel: "self", type: "application/atom+xml"
+ xml.link href: dashboard_url, rel: "alternate", type: "text/html"
xml.id projects_url
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
- if event.proper?
- xml.entry do
- event_link = event_feed_url(event)
- event_title = event_feed_title(event)
- event_summary = event_feed_summary(event)
-
- xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
- xml.link :href => event_link
- xml.title truncate(event_title, :length => 80)
- xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
- xml.author do |author|
- xml.name event.author_name
- xml.email event.author_email
- end
- xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? }
- end
- end
+ event_to_atom(xml, event)
end
end
diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml
index 15048a78063..8d6aaefb9ff 100644
--- a/app/views/devise/sessions/_oauth_providers.html.haml
+++ b/app/views/devise/sessions/_oauth_providers.html.haml
@@ -1,4 +1,4 @@
-- providers = (enabled_oauth_providers - [:ldap])
+- providers = additional_providers
- if providers.present?
.bs-callout.bs-callout-info{:'data-no-turbolink' => 'data-no-turbolink'}
%span Sign in with: &nbsp;
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 709d062df83..9b1d7d0416d 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -20,13 +20,13 @@
= link_to explore_groups_path(sort: nil) do
Name
= link_to explore_groups_path(sort: 'newest') do
- Newest
+ = sort_title_recently_created
= link_to explore_groups_path(sort: 'oldest') do
- Oldest
+ = sort_title_oldest_created
= link_to explore_groups_path(sort: 'recently_updated') do
- Recently updated
+ = sort_title_recently_updated
= link_to explore_groups_path(sort: 'last_updated') do
- Last updated
+ = sort_title_oldest_updated
%hr
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index f797c4e3830..02586077d8c 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -20,13 +20,13 @@
= link_to explore_projects_path(sort: nil) do
Name
= link_to explore_projects_path(sort: 'newest') do
- Newest
+ = sort_title_recently_created
= link_to explore_projects_path(sort: 'oldest') do
- Oldest
+ = sort_title_oldest_created
= link_to explore_projects_path(sort: 'recently_updated') do
- Recently updated
+ = sort_title_recently_updated
= link_to explore_projects_path(sort: 'last_updated') do
- Last updated
+ = sort_title_oldest_updated
%hr
.public-projects
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index f2005193f83..240001967f3 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -7,18 +7,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
- xml.entry do
- xml.id project_issue_url(issue.project, issue)
- xml.link :href => project_issue_url(issue.project, issue)
- xml.title truncate(issue.title, :length => 80)
- xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
- xml.author do |author|
- xml.name issue.author_name
- xml.email issue.author_email
- end
- xml.summary issue.title
- end
+ issue_to_atom(xml, issue)
end
end
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index e07bb7d2fb7..e765ea8338d 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -1,28 +1,12 @@
xml.instruct!
-xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
+xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do
xml.title "Group feed - #{@group.name}"
- xml.link :href => group_path(@group, :atom), :rel => "self", :type => "application/atom+xml"
- xml.link :href => group_path(@group), :rel => "alternate", :type => "text/html"
+ xml.link href: group_path(@group, :atom), rel: "self", type: "application/atom+xml"
+ xml.link href: group_path(@group), rel: "alternate", type: "text/html"
xml.id projects_url
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
- if event.proper?
- xml.entry do
- event_link = event_feed_url(event)
- event_title = event_feed_title(event)
-
- xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
- xml.link :href => event_link
- xml.title truncate(event_title, :length => 80)
- xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
- xml.author do |author|
- xml.name event.author_name
- xml.email event.author_email
- end
- xml.summary event_title
- end
- end
+ event_to_atom(xml, event)
end
end
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 2460a6a014d..04f79846858 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -4,7 +4,16 @@
= hidden_field_tag :group_id, @group.try(:id)
- if @project && @project.persisted?
= hidden_field_tag :project_id, @project.id
- = hidden_field_tag :search_code, true
+
+ - if current_controller?(:issues)
+ = hidden_field_tag :scope, 'issues'
+ - elsif current_controller?(:merge_requests)
+ = hidden_field_tag :scope, 'merge_requests'
+ - elsif current_controller?(:wikis)
+ = hidden_field_tag :scope, 'wiki_blobs'
+ - else
+ = hidden_field_tag :search_code, true
+
- if @snippet || @snippets
= hidden_field_tag :snippets, true
= hidden_field_tag :repository_ref, @ref
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index d6b52f86154..640104fdad1 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -83,7 +83,7 @@
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-user-avatar-input hidden"
- .light The maximum file size allowed is 100KB.
+ .light The maximum file size allowed is 200KB.
- if @user.avatar?
%hr
= link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
diff --git a/app/views/projects/_issuable_filter.html.haml b/app/views/projects/_issuable_filter.html.haml
new file mode 100644
index 00000000000..b3e5efd938f
--- /dev/null
+++ b/app/views/projects/_issuable_filter.html.haml
@@ -0,0 +1,72 @@
+.issues-filters
+ .dropdown.inline
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-user
+ %span.light assignee:
+ - if @assignee.present?
+ %strong= @assignee.name
+ - elsif params[:assignee_id] == "0"
+ Unassigned
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to project_filter_path(assignee_id: nil) do
+ Any
+ = link_to project_filter_path(assignee_id: 0) do
+ Unassigned
+ - @assignees.sort_by(&:name).each do |user|
+ %li
+ = link_to project_filter_path(assignee_id: user.id) do
+ = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
+ = user.name
+
+ .dropdown.inline.prepend-left-10
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-user
+ %span.light author:
+ - if @author.present?
+ %strong= @author.name
+ - elsif params[:author_id] == "0"
+ Unassigned
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to project_filter_path(author_id: nil) do
+ Any
+ = link_to project_filter_path(author_id: 0) do
+ Unassigned
+ - @authors.sort_by(&:name).each do |user|
+ %li
+ = link_to project_filter_path(author_id: user.id) do
+ = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
+ = user.name
+
+ .dropdown.inline.prepend-left-10
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-clock-o
+ %span.light milestone:
+ - if @milestone.present?
+ %strong= @milestone.title
+ - elsif params[:milestone_id] == "0"
+ None (backlog)
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to project_filter_path(milestone_id: nil) do
+ Any
+ = link_to project_filter_path(milestone_id: 0) do
+ None (backlog)
+ - project_active_milestones.each do |milestone|
+ %li
+ = link_to project_filter_path(milestone_id: milestone.id) do
+ %strong= milestone.title
+ %small.light= milestone.expires_at
+
+ .pull-right
+ = render 'shared/sort_dropdown'
diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml
index 6cdfab933b4..b02f52a5aff 100644
--- a/app/views/projects/_issuable_form.html.haml
+++ b/app/views/projects/_issuable_form.html.haml
@@ -14,17 +14,20 @@
.form-group.issuable-description
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
- = render 'projects/zen', f: f, attr: :description,
- classes: 'description form-control'
- .col-sm-12.hint
- .pull-left
- Parsed with
- #{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}.
- .pull-right
- Attach images (JPG, PNG, GIF) by dragging &amp; dropping
- or #{link_to 'selecting them', '#', class: 'markdown-selector' }.
- .clearfix
- .error-alert
+
+ = render layout: 'projects/md_preview' do
+ = render 'projects/zen', f: f, attr: :description,
+ classes: 'description form-control'
+ .col-sm-12.hint
+ .pull-left
+ Parsed with
+ #{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}.
+ .pull-right
+ Attach images (JPG, PNG, GIF) by dragging &amp; dropping
+ or #{link_to 'selecting them', '#', class: 'markdown-selector' }.
+
+ .clearfix
+ .error-alert
%hr
.form-group
.issue-assignee
@@ -49,7 +52,7 @@
- else
%span.light No open milestones available.
&nbsp;
- = link_to 'Create new milestone', new_project_milestone_path(issuable.project)
+ = link_to 'Create new milestone', new_project_milestone_path(issuable.project), target: :blank
.form-group
= f.label :label_ids, class: 'control-label' do
%i.icon-tag
@@ -61,7 +64,7 @@
- else
%span.light No labels yet.
&nbsp;
- = link_to 'Create new label', new_project_label_path(issuable.project)
+ = link_to 'Create new label', new_project_label_path(issuable.project), target: :blank
.form-actions
- if issuable.new_record?
diff --git a/app/views/projects/_issues_nav.html.haml b/app/views/projects/_issues_nav.html.haml
index 1e14a2deb8c..18628eb6207 100644
--- a/app/views/projects/_issues_nav.html.haml
+++ b/app/views/projects/_issues_nav.html.haml
@@ -2,7 +2,7 @@
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= link_to project_issues_path(@project), class: "tab" do
- Browse Issues
+ Issues
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to project_merge_requests_path(@project), class: "tab" do
@@ -19,7 +19,7 @@
- if current_controller?(:issues)
- if current_user
- %li
+ %li.hidden-xs
= link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
%i.fa.fa-rss
@@ -27,14 +27,17 @@
.pull-right
%button.btn.btn-default.sidebar-expand-button
%i.icon.fa.fa-list
- = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
- .append-right-10.hidden-xs.hidden-sm
- = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
- = hidden_field_tag :state, params['state']
- = hidden_field_tag :scope, params['scope']
- = hidden_field_tag :assignee_id, params['assignee_id']
- = hidden_field_tag :milestone_id, params['milestone_id']
- = hidden_field_tag :label_id, params['label_id']
+
+ .pull-left
+ = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
+ .append-right-10.hidden-xs.hidden-sm
+ = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
+ = hidden_field_tag :state, params['state']
+ = hidden_field_tag :scope, params['scope']
+ = hidden_field_tag :assignee_id, params['assignee_id']
+ = hidden_field_tag :milestone_id, params['milestone_id']
+ = hidden_field_tag :label_id, params['label_id']
+
- if can? current_user, :write_issue, @project
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus
@@ -45,8 +48,8 @@
.pull-right
%button.btn.btn-default.sidebar-expand-button
%i.icon.fa.fa-list
-
+
- if can? current_user, :write_merge_request, @project
- = link_to new_project_merge_request_path(@project), class: "pull-right btn btn-new", title: "New Merge Request" do
+ = link_to new_project_merge_request_path(@project), class: "btn btn-new pull-left", title: "New Merge Request" do
%i.fa.fa-plus
New Merge Request
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
new file mode 100644
index 00000000000..cb75149434f
--- /dev/null
+++ b/app/views/projects/_md_preview.html.haml
@@ -0,0 +1,13 @@
+%ul.nav.nav-tabs
+ %li.active
+ = link_to '#md-write-holder', class: 'js-md-write-button' do
+ Write
+ %li
+ = link_to '#md-preview-holder', class: 'js-md-preview-button',
+ data: { url: markdown_preview_project_path(@project) } do
+ Preview
+%div
+ .md-write-holder
+ = yield
+ .md-preview-holder.hide
+ .js-md-preview
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 9f2b1b59292..d2aefd815a1 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -20,9 +20,9 @@
= link_to project_branches_path(sort: nil) do
Name
= link_to project_branches_path(sort: 'recently_updated') do
- Recently updated
+ = sort_title_recently_updated
= link_to project_branches_path(sort: 'last_updated') do
- Last updated
+ = sort_title_oldest_updated
%hr
- unless @branches.empty?
%ul.bordered-list.top-list.all-branches
diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml
deleted file mode 100644
index 89710d3a09a..00000000000
--- a/app/views/projects/create.js.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-- if @project.saved?
- - if @project.import?
- :plain
- location.href = "#{import_project_path(@project)}";
- - else
- :plain
- location.href = "#{project_path(@project)}";
-- else
- :plain
- $(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
- $('.project-submit').enable();
- $('.save-project-loader').hide();
- $('.project-edit-container').show();
diff --git a/app/views/projects/import.html.haml b/app/views/projects/import.html.haml
deleted file mode 100644
index 4513c89e784..00000000000
--- a/app/views/projects/import.html.haml
+++ /dev/null
@@ -1,31 +0,0 @@
-- if @project.import_in_progress?
- .save-project-loader
- .center
- %h2
- %i.fa.fa-spinner.fa-spin
- Import in progress.
- %p.monospace git clone --bare #{hidden_pass_url(@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 git 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 4 minutes. For big repositories, use a clone/push combination.
- For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
- .form-actions
- = f.submit 'Retry import', class: "btn btn-create", tabindex: 4
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
new file mode 100644
index 00000000000..6c3083e49f5
--- /dev/null
+++ b/app/views/projects/imports/new.html.haml
@@ -0,0 +1,21 @@
+%h3.page-title
+ - if @project.import_failed?
+ Import failed. Retry?
+ - else
+ Import repository
+
+%hr
+
+= form_for @project, url: project_import_path(@project), method: :post, html: { class: 'form-horizontal' } do |f|
+ .form-group.import-url-data
+ = f.label :import_url, class: 'control-label' do
+ %span Import existing git 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 4 minutes. For big repositories, use a clone/push combination.
+ For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
+ .form-actions
+ = f.submit 'Start import', class: "btn btn-create", tabindex: 4
diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml
new file mode 100644
index 00000000000..2d1fdafed24
--- /dev/null
+++ b/app/views/projects/imports/show.html.haml
@@ -0,0 +1,9 @@
+.save-project-loader
+ .center
+ %h2
+ %i.fa.fa-spinner.fa-spin
+ Import in progress.
+ %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)}
+ %p Please wait while we import the repository for you. Refresh at will.
+ :javascript
+ new ProjectImport();
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 7525812696f..dc6510be858 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -4,7 +4,6 @@
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
.issue-title
- %span.light= "##{issue.iid}"
%span.str-truncated
= link_to_gfm issue.title, project_issue_path(issue.project, issue), class: "row_title"
- if issue.closed?
@@ -12,6 +11,7 @@
CLOSED
.issue-info
+ %span.light= "##{issue.iid}"
- if issue.assignee
assigned to #{link_to_member(@project, issue.assignee)}
- if issue.votes_count > 0
@@ -28,7 +28,7 @@
%span.task-status
= issue.task_status
- .pull-right
+ .pull-right.issue-updated-at
%small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
.issue-labels
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index 0bff8bdbead..15c84c7ced2 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -1,55 +1,7 @@
.append-bottom-10
.check-all-holder
= check_box_tag "check_all_issues", nil, false, class: "check_all_issues left"
- .issues-filters
- .dropdown.inline
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-user
- %span.light assignee:
- - if @assignee.present?
- %strong= @assignee.name
- - elsif params[:assignee_id] == "0"
- Unassigned
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(assignee_id: nil) do
- Any
- = link_to project_filter_path(assignee_id: 0) do
- Unassigned
- - @assignees.sort_by(&:name).each do |user|
- %li
- = link_to project_filter_path(assignee_id: user.id) do
- = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
- = user.name
-
- .dropdown.inline.prepend-left-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-clock-o
- %span.light milestone:
- - if @milestone.present?
- %strong= @milestone.title
- - elsif params[:milestone_id] == "0"
- None (backlog)
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(milestone_id: nil) do
- Any
- = link_to project_filter_path(milestone_id: 0) do
- None (backlog)
- - project_active_milestones.each do |milestone|
- %li
- = link_to project_filter_path(milestone_id: milestone.id) do
- %strong= milestone.title
- %small.light= milestone.expires_at
-
- .pull-right
- = render 'shared/sort_dropdown'
+ = render 'projects/issuable_filter'
.clearfix
.issues_bulk_update.hide
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index 012ba235951..61e651da932 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -7,17 +7,6 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
- xml.entry do
- xml.id project_issue_url(@project, issue)
- xml.link :href => project_issue_url(@project, issue)
- xml.title truncate(issue.title, :length => 80)
- xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
- xml.author do |author|
- xml.name issue.author_name
- xml.email issue.author_email
- end
- xml.summary issue.title
- end
+ issue_to_atom(xml, issue)
end
end
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index aad58e48f6c..01a1fabda26 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -38,6 +38,10 @@
- else
Open
+ .cross-project-ref
+ %i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'}
+ = cross_project_reference(@project, @issue)
+
.creator
Created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)}
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 1ee2e1bdae8..dedb060a231 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,13 +1,12 @@
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
- %span.light= "##{merge_request.iid}"
= link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title"
- if merge_request.merged?
%small.pull-right
%i.fa.fa-check
MERGED
- else
- %span.pull-right
+ %span.pull-right.hidden-xs
- if merge_request.for_fork?
%span.light
#{merge_request.source_project_namespace}:
@@ -15,6 +14,7 @@
%i.fa.fa-angle-right.light
= merge_request.target_branch
.merge-request-info
+ %span.light= "##{merge_request.iid}"
- if merge_request.author
authored by #{link_to_member(merge_request.source_project, merge_request.author)}
- if merge_request.votes_count > 0
@@ -31,7 +31,7 @@
%span.task-status
= merge_request.task_status
- .pull-right
+ .pull-right.hidden-xs
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
.merge-request-labels
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index d4666eacd7e..76813e688b5 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -21,12 +21,13 @@
.form-group
.light
= f.label :description, "Description"
- = render 'projects/zen', f: f, attr: :description,
- classes: 'description form-control'
- .clearfix.hint
- .pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
- .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
- .error-alert
+ = render layout: 'projects/md_preview' do
+ = render 'projects/zen', f: f, attr: :description,
+ classes: 'description form-control'
+ .clearfix.hint
+ .pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
+ .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+ .error-alert
.form-group
.issue-assignee
= f.label :assignee_id do
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index cd1e48ca976..b93e0f9da3e 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,62 +1,12 @@
= render "projects/issues_nav"
.row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
.col-md-3.responsive-side
= render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project),
labels: true, redirect: 'merge_requests', entity: 'merge_request'
.col-md-9
- .mr-filters.append-bottom-10
- .dropdown.inline
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-user
- %span.light assignee:
- - if @assignee.present?
- %strong= @assignee.name
- - elsif params[:assignee_id] == "0"
- Unassigned
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(assignee_id: nil) do
- Any
- = link_to project_filter_path(assignee_id: 0) do
- Unassigned
- - @assignees.sort_by(&:name).each do |user|
- %li
- = link_to project_filter_path(assignee_id: user.id) do
- = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
- = user.name
-
- .dropdown.inline.prepend-left-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-clock-o
- %span.light milestone:
- - if @milestone.present?
- %strong= @milestone.title
- - elsif params[:milestone_id] == "0"
- None (backlog)
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(milestone_id: nil) do
- Any
- = link_to project_filter_path(milestone_id: 0) do
- None (backlog)
- - project_active_milestones.each do |milestone|
- %li
- = link_to project_filter_path(milestone_id: milestone.id) do
- %strong= milestone.title
- %small.light= milestone.expires_at
-
- .pull-right
- = render 'shared/sort_dropdown'
-
+ .append-bottom-10
+ = render 'projects/issuable_filter'
.panel.panel-default
%ul.well-list.mr-list
= render @merge_requests
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index 7e5a4eda508..866b236d827 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -8,6 +8,10 @@
- else
Open
+ .cross-project-ref
+ %i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'}
+ = cross_project_reference(@project, @merge_request)
+
.creator
Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
diff --git a/app/views/projects/merge_requests/show/_state_widget.html.haml b/app/views/projects/merge_requests/show/_state_widget.html.haml
index 87dad6140be..a4f2a890969 100644
--- a/app/views/projects/merge_requests/show/_state_widget.html.haml
+++ b/app/views/projects/merge_requests/show/_state_widget.html.haml
@@ -11,14 +11,18 @@
- if @merge_request.closed?
%h4
- Closed by #{link_to_member(@project, @merge_request.closed_event.author, avatar: false)}
- #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
+ Closed
+ - if @merge_request.closed_event
+ by #{link_to_member(@project, @merge_request.closed_event.author, avatar: false)}
+ #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
%p Changes were not merged into target branch
- if @merge_request.merged?
%h4
- Merged by #{link_to_member(@project, @merge_request.merge_event.author, avatar: false)}
- #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
+ Merged
+ - if @merge_request.merge_event
+ by #{link_to_member(@project, @merge_request.merge_event.author, avatar: false)}
+ #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
= render "projects/merge_requests/show/remove_source_branch"
- if @merge_request.locked?
@@ -44,4 +48,3 @@
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
= succeed '.' do
!= gfm(issues_sentence(@closes_issues))
-
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 5fb01a11cc5..0f51a347f01 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -18,13 +18,14 @@
.col-sm-10
= f.text_field :title, maxlength: 255, class: "form-control"
%p.hint Required
- .form-group
+ .form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
- = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
- .hint
- .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
- .pull-left Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+ = render layout: 'projects/md_preview' do
+ = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
+ .hint
+ .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
+ .pull-left Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.clearfix
.error-alert
.col-md-6
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index f5cd0f21e01..e77ef84f51c 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -3,7 +3,7 @@
= render 'projects/errors'
.project-edit-content
- = form_for @project, remote: true, html: { class: 'new_project form-horizontal' } do |f|
+ = form_for @project, html: { class: 'new_project form-horizontal' } do |f|
.form-group.project-name-holder
= f.label :name, class: 'control-label' do
%strong Project name
diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml
new file mode 100644
index 00000000000..dd576243510
--- /dev/null
+++ b/app/views/projects/no_repo.html.haml
@@ -0,0 +1,22 @@
+%h2
+ %i.fa.fa-warning
+ No repository
+
+%p.slead
+ The repository for this project does not exist.
+ %br
+ This means you can not push code until you create an empty repository or import existing one.
+%hr
+
+.no-repo-actions
+ = link_to project_repository_path(@project), method: :post, class: 'btn btn-primary' do
+ Create empty bare repository
+
+ %strong.prepend-left-10.append-right-10 or
+
+ = link_to new_project_import_path(@project), class: 'btn' do
+ Import repository
+
+- 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"
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index c68b3817e79..47ffe1fd2f3 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -5,23 +5,13 @@
= f.hidden_field :noteable_id
= f.hidden_field :noteable_type
- %ul.nav.nav-tabs
- %li.active
- = link_to '#note-write-holder', class: 'js-note-write-button' do
- Write
- %li
- = link_to '#note-preview-holder', class: 'js-note-preview-button', data: { url: preview_project_notes_path(@project) } do
- Preview
- %div
- .note-write-holder
- = render 'projects/zen', f: f, attr: :note,
- classes: 'note_text js-note-text'
- .light.clearfix
- .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
- .pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
+ = render layout: 'projects/md_preview' do
+ = render 'projects/zen', f: f, attr: :note,
+ classes: 'note_text js-note-text'
+ .light.clearfix
+ .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
+ .pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
- .note-preview-holder.hide
- .js-note-preview
.note-form-actions
.buttons
@@ -29,7 +19,7 @@
= yield(:note_actions)
%a.btn.grouped.js-close-discussion-note-form Cancel
- .note-form-option
+ .note-form-option.hidden-xs
%a.choose-btn.btn.js-choose-note-attachment-button
%i.fa.fa-paperclip
%span Choose File ...
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index a25c5e207fb..354afd3e2c9 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -18,6 +18,8 @@
%i.fa.fa-trash-o.cred
Remove
= link_to_member(@project, note.author, avatar: false)
+ %span.author-username
+ = '@' + note.author.username
%span.note-last-update
= note_timestamp(note)
@@ -38,7 +40,8 @@
.note-edit-form
= form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f|
- = f.text_area :note, class: 'note_text js-note-text js-gfm-input turn-on'
+ = render layout: 'projects/md_preview' do
+ = f.text_area :note, class: 'note_text js-note-text markdown-area js-gfm-input turn-on'
.form-actions.clearfix
= f.submit 'Save changes', class: "btn btn-primary btn-save js-comment-button"
diff --git a/app/views/search/_project_filter.html.haml b/app/views/search/_project_filter.html.haml
index c201b3d6c47..ad933502a28 100644
--- a/app/views/search/_project_filter.html.haml
+++ b/app/views/search/_project_filter.html.haml
@@ -25,6 +25,7 @@
= @search_results.notes_count
%li{class: ("active" if @scope == 'wiki_blobs')}
= link_to search_filter_path(scope: 'wiki_blobs') do
+ %i.fa.fa-book
Wiki
.pull-right
= @search_results.wiki_blobs_count
diff --git a/app/views/shared/_choose_group_avatar_button.html.haml b/app/views/shared/_choose_group_avatar_button.html.haml
index f32c2d388a7..299c0bd42a2 100644
--- a/app/views/shared/_choose_group_avatar_button.html.haml
+++ b/app/views/shared/_choose_group_avatar_button.html.haml
@@ -4,4 +4,4 @@
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: 'js-group-avatar-input hidden'
-.light The maximum file size allowed is 100KB.
+.light The maximum file size allowed is 200KB.
diff --git a/app/views/shared/_promo.html.haml b/app/views/shared/_promo.html.haml
index 3400c345c4c..3596aabe309 100644
--- a/app/views/shared/_promo.html.haml
+++ b/app/views/shared/_promo.html.haml
@@ -1,5 +1,5 @@
.gitlab-promo
= link_to 'Homepage', promo_url
= link_to "Blog", promo_url + '/blog/'
- = link_to "@gitlabhq", "https://twitter.com/gitlabhq"
+ = link_to "@gitlab", "https://twitter.com/gitlab"
= link_to "Requests", "http://feedback.gitlab.com/"
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index 7b37b39780e..54f59245690 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -9,13 +9,13 @@
%ul.dropdown-menu
%li
= link_to project_filter_path(sort: 'newest') do
- Newest
+ = sort_title_recently_created
= link_to project_filter_path(sort: 'oldest') do
- Oldest
+ = sort_title_oldest_created
= link_to project_filter_path(sort: 'recently_updated') do
- Recently updated
+ = sort_title_recently_updated
= link_to project_filter_path(sort: 'last_updated') do
- Last updated
+ = sort_title_oldest_updated
= link_to project_filter_path(sort: 'milestone_due_soon') do
Milestone due soon
= link_to project_filter_path(sort: 'milestone_due_later') do
diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder
new file mode 100644
index 00000000000..b7216a88765
--- /dev/null
+++ b/app/views/users/show.atom.builder
@@ -0,0 +1,12 @@
+xml.instruct!
+xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlnsmedia" => "http://search.yahoo.com/mrss/" do
+ xml.title "Activity feed for #{@user.name}"
+ xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml"
+ xml.link href: user_url(@user), rel: "alternate", type: "text/html"
+ xml.id projects_url
+ xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+
+ @events.each do |event|
+ event_to_atom(xml, event)
+ end
+end
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index cb49c030af2..54f2666ce5d 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -18,7 +18,15 @@
%h4 Groups:
= render 'groups', groups: @groups
%hr
- %h4 User Activity:
+ %h4
+ User Activity:
+
+ - if current_user
+ %span.rss-icon.pull-right
+ = link_to user_path(@user, :atom, { private_token: current_user.private_token }) do
+ %strong
+ %i.fa.fa-rss
+
= render @events
.col-md-4
= render 'profile', user: @user
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
new file mode 100644
index 00000000000..cc0a7f25664
--- /dev/null
+++ b/app/workers/project_service_worker.rb
@@ -0,0 +1,9 @@
+class ProjectServiceWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :project_web_hook
+
+ def perform(hook_id, data)
+ Service.find(hook_id).execute(data)
+ end
+end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index bb0ffae0b70..7b4c180fccc 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -35,7 +35,7 @@ production: &base
## Date & Time settings
# Uncomment and customize if you want to change the default time zone of GitLab application.
- # To see all available zones, run `bundle exec rake time:zones:all`
+ # To see all available zones, run `bundle exec rake time:zones:all RAILS_ENV=production`
# time_zone: 'UTC'
## Email settings
diff --git a/config/initializers/4_sidekiq.rb b/config/initializers/4_sidekiq.rb
index 228b14cb526..e856499732e 100644
--- a/config/initializers/4_sidekiq.rb
+++ b/config/initializers/4_sidekiq.rb
@@ -14,7 +14,8 @@ Sidekiq.configure_server do |config|
}
config.server_middleware do |chain|
- chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger
+ chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
+ chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS']
end
end
diff --git a/config/routes.rb b/config/routes.rb
index 470fe7f4dc1..533e044ca4c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -137,7 +137,8 @@ Gitlab::Application.routes.draw do
end
end
- match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ }, via: :get
+ match "/u/:username" => "users#show", as: :user,
+ constraints: {username: /(?:[^.]|\.(?!atom$))+/, format: /atom/}, via: :get
#
# Dashboard Area
@@ -185,13 +186,12 @@ Gitlab::Application.routes.draw do
post :unarchive
post :upload_image
post :toggle_star
+ get :markdown_preview
get :autocomplete_sources
- get :import
- put :retry_import
end
scope module: :projects do
- resources :blob, only: [:show, :destroy], constraints: { id: /.+/ } do
+ resources :blob, only: [:show, :destroy], constraints: { id: /.+/, format: false } do
get :diff, on: :member
end
resources :raw, only: [:show], constraints: {id: /.+/}
@@ -232,8 +232,9 @@ Gitlab::Application.routes.draw do
end
resource :fork, only: [:new, :create]
+ resource :import, only: [:new, :create, :show]
- resource :repository, only: [:show] do
+ resource :repository, only: [:show, :create] do
member do
get "archive", constraints: { format: Gitlab::Regex.archive_formats_regex }
end
@@ -329,10 +330,6 @@ Gitlab::Application.routes.draw do
member do
delete :delete_attachment
end
-
- collection do
- post :preview
- end
end
end
end
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index ea22744fd90..d8b4f5c7c32 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -13,9 +13,9 @@
#
# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
-# Use at least one worker per core if you're on a dedicated server,
-# more will usually help for _short_ waits on databases/caches.
-# The minimum is 2
+# Read about unicorn workers here:
+# http://doc.gitlab.com/ee/install/requirements.html#unicorn-workers
+#
worker_processes 2
# Since Unicorn is never exposed to outside clients, it does not need to
@@ -37,10 +37,10 @@ listen "127.0.0.1:8080", :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
#
-# NOTICE: git push over http depends on this value.
-# If you want be able to push huge amount of data to git repository over http
-# you will have to increase this value too.
-#
+# NOTICE: git push over http depends on this value.
+# If you want be able to push huge amount of data to git repository over http
+# you will have to increase this value too.
+#
# Example of output if you try to push 1GB repo to GitLab over http.
# -> git push http://gitlab.... master
#
diff --git a/db/migrate/20141121161704_add_identity_table.rb b/db/migrate/20141121161704_add_identity_table.rb
new file mode 100644
index 00000000000..6fe63637dfe
--- /dev/null
+++ b/db/migrate/20141121161704_add_identity_table.rb
@@ -0,0 +1,38 @@
+class AddIdentityTable < ActiveRecord::Migration
+ def up
+ create_table :identities do |t|
+ t.string :extern_uid
+ t.string :provider
+ t.references :user
+ end
+
+ add_index :identities, :user_id
+
+ execute <<eos
+INSERT INTO identities (provider, extern_uid, user_id)
+SELECT provider, extern_uid, id FROM users
+WHERE provider IS NOT NULL
+eos
+
+ remove_column :users, :extern_uid
+ remove_column :users, :provider
+ end
+
+ def down
+ add_column :users, :extern_uid, :string
+ add_column :users, :provider, :string
+
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
+ execute <<eos
+UPDATE users u
+SET provider = i.provider, extern_uid = i.extern_uid
+FROM identities i
+WHERE i.user_id = u.id
+eos
+ else
+ execute "UPDATE users u, identities i SET u.provider = i.provider, u.extern_uid = i.extern_uid WHERE u.id = i.user_id"
+ end
+
+ drop_table :identities
+ end
+end
diff --git a/db/migrate/20141205134006_add_locked_at_to_merge_request.rb b/db/migrate/20141205134006_add_locked_at_to_merge_request.rb
new file mode 100644
index 00000000000..49651c44a82
--- /dev/null
+++ b/db/migrate/20141205134006_add_locked_at_to_merge_request.rb
@@ -0,0 +1,5 @@
+class AddLockedAtToMergeRequest < ActiveRecord::Migration
+ def change
+ add_column :merge_requests, :locked_at, :datetime
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 68d1080b6ee..b8335c5841b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20141121133009) do
+ActiveRecord::Schema.define(version: 20141205134006) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -74,6 +74,14 @@ ActiveRecord::Schema.define(version: 20141121133009) do
add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
+ create_table "identities", force: true do |t|
+ t.string "extern_uid"
+ t.string "provider"
+ t.integer "user_id"
+ end
+
+ add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
+
create_table "issues", force: true do |t|
t.string "title"
t.integer "assignee_id"
@@ -173,6 +181,7 @@ ActiveRecord::Schema.define(version: 20141121133009) do
t.integer "iid"
t.text "description"
t.integer "position", default: 0
+ t.datetime "locked_at"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
@@ -350,8 +359,6 @@ ActiveRecord::Schema.define(version: 20141121133009) do
t.string "bio"
t.integer "failed_attempts", default: 0
t.datetime "locked_at"
- t.string "extern_uid"
- t.string "provider"
t.string "username"
t.boolean "can_create_group", default: true, null: false
t.boolean "can_create_team", default: true, null: false
@@ -360,6 +367,7 @@ ActiveRecord::Schema.define(version: 20141121133009) 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"
@@ -367,7 +375,6 @@ ActiveRecord::Schema.define(version: 20141121133009) do
t.string "unconfirmed_email"
t.boolean "hide_no_ssh_key", default: false
t.string "website_url", default: "", null: false
- t.datetime "last_credential_check_at"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
@@ -375,7 +382,6 @@ ActiveRecord::Schema.define(version: 20141121133009) do
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
- add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
add_index "users", ["name"], name: "index_users_on_name", using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_index "users", ["username"], name: "index_users_on_username", using: :btree
diff --git a/doc/README.md b/doc/README.md
index b9aa12f7675..896224fe930 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -2,22 +2,22 @@
## User documentation
-- [API](api/README.md) Explore how you can access GitLab via a simple and powerful API.
-- [Markdown](markdown/markdown.md) Learn what you can do with GitLab's advanced formatting system.
+- [API](api/README.md) Automate GitLab via a simple and powerful API.
+- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
-- [Project Services](project_services/project_services.md) Explore how project services can integrate a project with external services, such as for CI.
-- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to a project.
+- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
+- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
-- [Workflow](workflow/README.md) Learn how to use Git and GitLab together.
+- [Workflow](workflow/README.md) Learn how to get the maximum out of GitLab.
## Administrator documentation
- [Install](install/README.md) Requirements, directory structures and manual installation.
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
-- [Raketasks](raketasks/README.md) Explore what GitLab has in store for you to make administration easier.
+- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
-- [System hooks](system_hooks/system_hooks.md) Let GitLab notify you when certain management tasks need to be carried out.
+- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
- [Update](update/README.md) Update guides to upgrade your installation.
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
diff --git a/doc/development/README.md b/doc/development/README.md
index 20db6662aca..c31e5d7ae97 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -4,3 +4,4 @@
- [Shell commands](shell_commands.md) in the GitLab codebase
- [Rake tasks](rake_tasks.md) for development
- [CI setup](ci_setup.md) for testing GitLab
+- [Sidekiq debugging](sidekiq_debugging.md)
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index c4813d22eaa..209182e7742 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -8,6 +8,38 @@ EE releases are available not long after CE releases. To obtain the GitLab EE th
Both EE and CE require an add-on component called gitlab-shell. It is obtained from the [gitlab-shell repository](https://gitlab.com/gitlab-org/gitlab-shell/tree/master). New versions are usually tags but staying on the master branch will give you the latest stable version. New releases are generally around the same time as GitLab CE releases with exception for informal security updates deemed critical.
+## Physical office analogy
+
+You can imagine GitLab as a physical office.
+
+**The repositories** are the goods GitLab handling.
+They can be stored in a warehouse.
+This can be either a hard disk, or something more complex, such as a NFS filesystem;
+
+**NginX** acts like the front-desk.
+Users come to NginX and request actions to be done by workers in the office;
+
+**The database** is a series of metal file cabinets with information on:
+ - The goods in the warehouse (metadata, issues, merge requests etc);
+ - The users coming to the front desk (permissions)
+
+**Redis** is a communication board with “cubby holes” that can contain tasks for office workers;
+
+**Sidekiq** is a worker that primarily handles sending out emails.
+It takes tasks from the Redis communication board;
+
+**A Unicorn worker** is a worker that handles quick/mundane tasks.
+They work with the communication board (Redis).
+Their job description:
+ - check permissions by checking the user session stored in a Redis “cubby hole”;
+ - make tasks for Sidekiq;
+ - fetch stuff from the warehouse or move things around in there;
+
+**Gitlab-shell** is a third kind of worker that takes orders from a fax machine (SSH) instead of the front desk (HTTP).
+Gitlab-shell communicates with Sidekiq via the “communication board” (Redis), and asks quick questions of the Unicorn workers either directly or via the front desk.
+
+**GitLab Enterprise Edition (the application)** is the collection of processes and business practices that the office is run by.
+
## System Layout
When referring to ~git in the pictures it means the home directory of the git user which is typically /home/git.
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 6d9ac161e91..53f8095cb13 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -1,6 +1,6 @@
# Rake tasks for developers
-## Setup db with developer seeds:
+## Setup db with developer seeds
Note that if your db user does not have advanced privileges you must create the db manually before running this command.
@@ -8,6 +8,10 @@ Note that if your db user does not have advanced privileges you must create the
bundle exec rake setup
```
+The `setup` task is a alias for `gitlab:setup`.
+This tasks calls `db:setup` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and fianlly it calls `db:seed_fu` to seed the database.
+Note: `db:setup` calls `db:seed` but this does nothing.
+
## Run tests
This runs all test suites present in GitLab.
diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md
index 23c8365c340..1e51ad73e32 100644
--- a/doc/development/shell_commands.md
+++ b/doc/development/shell_commands.md
@@ -1,5 +1,8 @@
# Guidelines for shell commands in the GitLab codebase
+This document contains guidelines for working with processes and files in the GitLab codebase.
+These guidelines are meant to make your code more reliable _and_ secure.
+
## References
- [Google Ruby Security Reviewer's Guide](https://code.google.com/p/ruby-security/wiki/Guide)
@@ -109,3 +112,63 @@ logs = IO.popen(%W(git log), chdir: repo_dir).read
```
Note that unlike `Gitlab::Popen.popen`, `IO.popen` does not capture standard error.
+
+## Avoid user input at the start of path strings
+
+Various methods for opening and reading files in Ruby can be used to read the
+standard output of a process instead of a file. The following two commands do
+roughly the same:
+
+```
+`touch /tmp/pawned-by-backticks`
+File.read('|touch /tmp/pawned-by-file-read')
+```
+
+The key is to open a 'file' whose name starts with a `|`.
+Affected methods include Kernel#open, File::read, File::open, IO::open and IO::read.
+
+You can protect against this behavior of 'open' and 'read' by ensuring that an
+attacker cannot control the start of the filename string you are opening. For
+instance, the following is sufficient to protect against accidentally starting
+a shell command with `|`:
+
+```
+# we assume repo_path is not controlled by the attacker (user)
+path = File.join(repo_path, user_input)
+# path cannot start with '|' now.
+File.read(path)
+```
+
+## Guard against path traversal
+
+Path traversal is a security where the program (GitLab) tries to restrict user
+access to a certain directory on disk, but the user manages to open a file
+outside that directory by taking advantage of the `../` path notation.
+
+```
+# Suppose the user gave us a path and they are trying to trick us
+user_input = '../other-repo.git/other-file'
+
+# We look up the repo path somewhere
+repo_path = 'repositories/user-repo.git'
+
+# The intention of the code below is to open a file under repo_path, but
+# because the user used '..' she can 'break out' into
+# 'repositories/other-repo.git'
+full_path = File.join(repo_path, user_input)
+File.open(full_path) do # Oops!
+```
+
+A good way to protect against this is to compare the full path with its
+'absolute path' according to Ruby's `File.absolute_path`.
+
+```
+full_path = File.join(repo_path, user_input)
+if full_path != File.absolute_path(full_path)
+ raise "Invalid path: #{full_path.inspect}"
+end
+
+File.open(full_path) do # Etc.
+```
+
+A check like this could have avoided CVE-2013-4583.
diff --git a/doc/development/sidekiq_debugging.md b/doc/development/sidekiq_debugging.md
new file mode 100644
index 00000000000..cea11e5f126
--- /dev/null
+++ b/doc/development/sidekiq_debugging.md
@@ -0,0 +1,14 @@
+# Sidekiq debugging
+
+## Log arguments to Sidekiq jobs
+
+If you want to see what arguments are being passed to Sidekiq jobs you can set
+the SIDEKIQ_LOG_ARGUMENTS environment variable.
+
+```
+SIDEKIQ_LOG_ARGUMENTS=1 bundle exec foreman start
+```
+
+It is not recommend to enable this setting in production because some Sidekiq
+jobs (such as sending a password reset email) take secret arguments (for
+example the password reset token).
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 5dd9388eece..f55a0e73199 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -6,9 +6,9 @@ Since a manual installation is a lot of work and error prone we strongly recomme
## Select Version to Install
-Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install. In most cases this should be the highest numbered stable branch (example shown below).
-
-![Select latest branch](https://i.imgur.com/Lrdxk1k.png)
+Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the tag (version) of GitLab you would like to install.
+In most cases this should be the highest numbered production tag (without rc in it).
+You can select the tag in the version dropdown in the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the [GitLab Blog](https://about.gitlab.com/blog/) for installation guide links by version.
@@ -54,7 +54,7 @@ up-to-date and install it.
Install the required packages (needed to compile Ruby and native extensions to Ruby gems):
- sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake
+ sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake libkrb5-dev
Make sure you have the right version of Git installed
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index fd59ac8a073..8eabb219b1b 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -38,6 +38,16 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
## Hardware requirements
+### Storage
+
+The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least twice as much free space as all your repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo.
+
+If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them.
+
+Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume.
+
+If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab.
+
### CPU
- 1 core works supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core
@@ -50,6 +60,10 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
### Memory
+You need at least 2GB of addressable memory (RAM + swap) to install and use GitLab!
+With less memory GitLab will give strange errors during the reconfigure run and 500 errors during usage.
+
+- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advise.
- 1GB RAM + 1GB swap supports up to 100 users
- **2GB RAM** is the **recommended** memory size and supports up to 500 users
- 4GB RAM supports up to 2,000 users
@@ -60,15 +74,16 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application.
-### Storage
+## Unicorn Workers
-The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least twice as much free space as all your repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo.
+It's possible to increase the amount of unicorn workers and tis will usually help for to reduce the response time of the applications.
+For most instances we recommend using: CPU cores + 1 = unicorn workers.
+So for a machine with 2 cores, 3 unicorn workers is ideal.
-If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them.
-
-Apart from a local hard drive you can also mount a volume that supports the network file system (NFS) protocol. This volume might be located on a file server, a network attached storage (NAS) device, a storage area network (SAN) or on an Amazon Web Services (AWS) Elastic Block Store (EBS) volume.
-
-If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab.
+For all machines that have 1GB and up we recommend a minimum of two unicorn workers.
+If you have a 512MB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping.
+With one Unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check).
+If you have a 512MB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping.
## Database
diff --git a/doc/integration/gitlab_buttons_in_gmail.md b/doc/integration/gitlab_buttons_in_gmail.md
index 5cfea5a90f8..0816509c557 100644
--- a/doc/integration/gitlab_buttons_in_gmail.md
+++ b/doc/integration/gitlab_buttons_in_gmail.md
@@ -9,3 +9,20 @@ If correctly setup, emails that require an action will be marked in Gmail.
To get this functioning, you need to be registered with Google.
[See how to register with google in this document.](https://developers.google.com/gmail/markup/registering-with-google)
+To aid the registering with google, GitLab offers a rake task that will send an email to google whitelisting email address from your GitLab server.
+
+To check what would be sent to the google email address, run the rake task:
+
+```bash
+bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production
+```
+
+**This will not send the email but give you the output of how the mail will look.**
+
+Copy the output of the rake task to [google email markup tester](https://www.google.com/webmasters/markup-tester/u/0/) and press "Validate".
+
+If you receive "No errors detected" message from the tester you can send the email using:
+
+```bash
+bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production SEND=true
+``
diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md
index d1b52927d30..b9e501c5ec1 100644
--- a/doc/integration/twitter.md
+++ b/doc/integration/twitter.md
@@ -13,7 +13,7 @@ To enable the Twitter OmniAuth provider you must register your application with
something else descriptive.
- Description: Create a description.
- Website: The URL to your GitLab installation. 'https://gitlab.example.com'
- - Callback URL: 'https://gitlab.example.com/users/auth/github/callback'
+ - Callback URL: 'https://gitlab.example.com/users/auth/twitter/callback'
- Agree to the "Rules of the Road."
![Twitter App Details](twitter_app_details.png)
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index d561868c8bb..e21384d21dc 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -19,6 +19,7 @@ If a user is a GitLab administrator they receive all permissions.
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
+| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ |
| Write a wiki | | | ✓ | ✓ | ✓ |
@@ -35,6 +36,8 @@ If a user is a GitLab administrator they receive all permissions.
| Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
+| Force push to protected branches | | | | | |
+| Remove protected branches | | | | | |
## Group
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index d2f0d6e7bc1..f9d2f5dc4eb 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -137,7 +137,7 @@ with the name of your bucket:
Please be informed that a backup does not store your configuration files.
If you use an Omnibus package please see the [instructions in the readme to backup your configuration](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#backup-and-restore-omnibus-gitlab-configuration).
If you have a cookbook installation there should be a copy of your configuration in Chef.
-If you have a manual installation please consider backing up your gitlab.yml file and any SSL keys and certificates.
+If you have a manual installation please consider backing up your `gitlab.yml` file, any SSL keys and certificates, and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
## Restore a previously created backup
@@ -203,5 +203,31 @@ Add the following lines at the bottom:
```
# Create a full backup of the GitLab repositories and SQL database every day at 4am
-0 4 * * * cd /home/git/gitlab && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake gitlab:backup:create RAILS_ENV=production
+0 4 * * * cd /home/git/gitlab && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake gitlab:backup:create RAILS_ENV=production CRON=1
```
+
+The `CRON=1` environment setting tells the backup script to suppress all progress output if there are no errors.
+This is recommended to reduce cron spam.
+
+## Alternative backup strategies
+
+If your GitLab server contains a lot of Git repository data you may find the GitLab backup script to be too slow.
+In this case you can consider using filesystem snapshots as part of your backup strategy.
+
+Example: Amazone EBS
+
+> A GitLab server using omnibus-gitlab hosted on Amazon AWS.
+> An EBS drive containing an ext4 filesystem is mounted at `/var/opt/gitlab`.
+> In this case you could make an application backup by taking an EBS snapshot.
+> The backup includes all repositories, uploads and Postgres data.
+
+Example: LVM snapshots + Rsync
+
+> A GitLab server using omnibus-gitlab, with an LVM logical volume mounted at `/var/opt/gitlab`.
+> Replicating the `/var/opt/gitlab` directory usign Rsync would not be reliable because too many files would change while Rsync is running.
+> Instead of rsync-ing `/var/opt/gitlab`, we create a temporary LVM snapshot, which we mount as a read-only filesystem at `/mnt/gitlab_backup`.
+> Now we can have a longer running Rsync job which will create a consistent replica on the remote server.
+> The replica includes all repositories, uploads and Postgres data.
+
+If you are running GitLab on a virtualized server you can possibly also create VM snapshots of the entire GitLab server.
+It is not uncommon however for a VM snapshot to require you to power down the server, so this approach is probably of limited practical use.
diff --git a/doc/release/monthly.md b/doc/release/monthly.md
index b391d8ca6ab..3f1bccb2cf6 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -8,7 +8,9 @@ NOTE: This is a guide for GitLab developers.
### **2. Release Manager**
-A release manager is selected that coordinates the entire release of this version. The release manager has to make sure all the steps below are done and delegated where necessary. This person should also make sure this document is kept up to date and issues are created and updated.
+A release manager is selected that coordinates all releases the coming month, including the patch releases for previous releases.
+The release manager has to make sure all the steps below are done and delegated where necessary.
+This person should also make sure this document is kept up to date and issues are created and updated.
### **3. Create an overall issue**
@@ -18,36 +20,40 @@ Replace the dates with actual dates based on the number of workdays before the r
```
Xth:
-* Update the changelog (#LINK)
-* Triage the omnibus-gitlab milestone
+- [ ] Update the CE changelog (#LINK)
+- [ ] Update the EE changelog (#LINK)
+- [ ] Update the CI changelog (#LINK)
+- [ ] Triage the omnibus-gitlab milestone
Xth:
-* Merge CE in to EE (#LINK)
-* Close the omnibus-gitlab milestone
+- [ ] Merge CE in to EE (#LINK)
+- [ ] Close the omnibus-gitlab milestone
Xth:
-* Create x.x.0.rc1 (#LINK)
-* Build package for GitLab.com (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#build-a-package)
+- [ ] Create x.x.0.rc1 (#LINK)
+- [ ] Create x.x.0.rc1-ee (#LINK)
+- [ ] Create CI y.y.0.rc1 (#LINK)
+- [ ] Build package for GitLab.com (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#build-a-package)
Xth:
-* Update GitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
-* Regression issue and tweet about rc1 (#LINK)
-* Start blog post (#LINK)
+- [ ] Update GitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
+- [ ] Regression issues (CE, CI) and tweet about rc1 (#LINK)
+- [ ] Start blog post (#LINK)
Xth:
-* Do QA and fix anything coming out of it (#LINK)
+- [ ] Do QA and fix anything coming out of it (#LINK)
22nd:
-* Release CE and EE (#LINK)
+- [ ] Release CE, EE and CI (#LINK)
Xth:
-* * Deploy to GitLab.com (#LINK)
+- [ ] Deploy to GitLab.com (#LINK)
```
@@ -55,6 +61,8 @@ Xth:
Any changes not yet added to the changelog are added by lead developer and in that merge request the complete team is asked if there is anything missing.
+There are three changelogs that need to be updated: CE, EE and CI.
+
### **5. Take weekend and vacations into account**
Ensure that there is enough time to incorporate the findings of the release candidate, etc.
@@ -79,6 +87,7 @@ The RC1 release comes with the task to update the installation and upgrade docs.
1. Create: CE update guide from previous version. Like `7.3-to-7.4.md`
1. Create: CE to EE update guide in EE repository for latest version.
1. Update: `6.x-or-7.x-to-7.x.md` to latest version.
+1. Create: CI update guide from previous version
It's best to copy paste the previous guide and make changes where necessary.
The typical steps are listed below with any points you should specifically look at.
@@ -171,6 +180,24 @@ Now developers can use master for merging new features.
So you should use stable branch for future code chages related to release.
+### 5. Release GitLab CI RC1
+
+Add to your local `gitlab-ci/.git/config`:
+
+```
+[remote "public"]
+ url = none
+ pushurl = git@dev.gitlab.org:gitlab/gitlab-ci.git
+ pushurl = git@gitlab.com:gitlab-org/gitlab-ci.git
+ pushurl = git@github.com:gitlabhq/gitlab-ci.git
+```
+
+* Create a stable branch `x-y-stable`
+* Bump VERSION to `x.y.0.rc1`
+* `git tag -a v$(cat VERSION) -m "Version $(cat VERSION)"
+* `git push public x-y-stable v$(cat VERSION)`
+
+
# **4 workdays before release - Release RC1**
### **1. Determine QA person
@@ -189,6 +216,8 @@ It is important to do this as soon as possible, so we can catch any errors befor
- Start with a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) and fill it out.
- Check the changelog of CE and EE for important changes.
+- Also check the CI changelog
+- Add a proposed tweet text to the blog post WIP MR description.
- Create a WIP MR for the blog post
- Ask Dmitriy to add screenshots to the WIP MR.
- Decide with team who will be the MVP user.
@@ -236,7 +265,7 @@ Create an issue with description of a problem, if it is quick fix fix it yoursel
**NOTE** If there is a problem that cannot be fixed in a timely manner, reverting the feature is an option! If the feature is reverted,
create an issue about it in order to discuss the next steps after the release.
-# **22nd - Release CE and EE**
+# **22nd - Release CE, EE and CI**
**Make sure EE `x-x-stable-ee` has latest changes from CE `x-x-stable`**
@@ -256,6 +285,11 @@ Bump version, create release tag and push to remotes:
bundle exec rake release["x.x.0"]
```
+Also perform these steps for GitLab CI:
+
+- bump version in the stable branch
+- create annotated tag
+- push the stable branch and the annotated tag to the public repositories
### **2. Update installation.md**
@@ -286,17 +320,4 @@ Proposed tweet for CE "GitLab X.X is released! It brings *** <link-to-blogpost>"
# **1 workday after release - Update GitLab.com**
- Build a package for gitlab.com based on the official release instead of RC1
-- Deploy the package
-
-# **25th - Release GitLab CI**
-
-- Create the update guid `doc/x.x-to-x.x.md`.
-- Update CHANGELOG
-- Bump version
-- Create annotated tags `git tag -a vx.x.0 -m 'Version x.x.0' xxxxx`
-- Create stable branch `x-x-stable`
-- Create GitHub release post
-- Post to blog about release
-- Post to twitter
-
-
+- Deploy the package (should not need downtime because of the small difference with RC1)
diff --git a/doc/release/patch.md b/doc/release/patch.md
index 6ed56427e9a..2bd34b7d822 100644
--- a/doc/release/patch.md
+++ b/doc/release/patch.md
@@ -17,13 +17,15 @@ Otherwise include it in the monthly release and note there was a regression fix
1. Create an issue on private GitLab development server
1. Name the issue "Release X.X.X CE and X.X.X EE", this will make searching easier
1. Fix the issue on a feature branch, do this on the private GitLab development server
+1. If it is a security issue, then assign it to the release manager and apply a 'security' label
+1. Build the package for GitLab.com and do a deploy
1. Consider creating and testing workarounds
1. After the branch is merged into master, cherry pick the commit(s) into the current stable branch
1. Make sure that the build has passed and all tests are passing
1. In a separate commit in the stable branch update the CHANGELOG
1. For EE, update the CHANGELOG-EE if it is EE specific fix. Otherwise, merge the stable CE branch and add to CHANGELOG-EE "Merge community edition changes for version X.X.X"
-### Bump version
+### Bump version
Get release tools
diff --git a/doc/release/security.md b/doc/release/security.md
index 79d23c02ea4..b67e0f37a04 100644
--- a/doc/release/security.md
+++ b/doc/release/security.md
@@ -14,11 +14,13 @@ Please report suspected security vulnerabilities in private to <support@gitlab.c
1. Verify that the issue can be reproduced
1. Acknowledge the issue to the researcher that disclosed it
+1. Inform the release manager that there needs to be a security release
1. Do the steps from [patch release document](doc/release/patch.md), starting with "Create an issue on private GitLab development server"
+1. The MR with the security fix should get a 'security' label and be assigned to the release manager
+1. Build the package for GitLab.com and do a deploy
1. Create feature branches for the blog post on GitLab.com and link them from the code branch
1. Merge and publish the blog posts
1. Send tweets about the release from `@gitlabhq`
-1. Send out an email to the 'GitLab Newsletter' mailing list on MailChimp (or the 'Subscribers' list if the security fix is for EE only)
1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq)
1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number
1. Add the security researcher to the [Security Researcher Acknowledgments list](http://about.gitlab.com/vulnerability-acknowledgements/)
diff --git a/doc/update/7.3-to-7.4.md b/doc/update/7.3-to-7.4.md
index f8a405c195b..2466050ea4c 100644
--- a/doc/update/7.3-to-7.4.md
+++ b/doc/update/7.3-to-7.4.md
@@ -165,6 +165,10 @@ mysql> \q
# Set production -> password: the password your replaced $password with earlier
sudo -u git -H editor /home/git/gitlab/config/database.yml
+# Start GitLab
+sudo service gitlab start
+sudo service nginx restart
+
# Run thorough check
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
diff --git a/doc/update/7.4-to-7.5.md b/doc/update/7.4-to-7.5.md
index c12becc1e14..673eab3c56e 100644
--- a/doc/update/7.4-to-7.5.md
+++ b/doc/update/7.4-to-7.5.md
@@ -71,21 +71,10 @@ There are new configuration options available for gitlab.yml. View them with the
git diff origin/7-4-stable:config/gitlab.yml.example origin/7-5-stable:config/gitlab.yml.example
```
-#### Change timeout for unicorn
-
-```
-# set timeout to 60
-sudo -u git -H editor config/unicorn.rb
-```
-
-#### Change nginx https settings
-
-* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/lib/support/nginx/gitlab-ssl but with your setting
-
-#### MySQL Databases: Update database.yml config file
-
-* Add `collation: utf8_general_ci` to config/database.yml as seen in [config/database.yml.mysql](config/database.yml.mysql)
+#### Change Nginx settings
+* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings
+* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your setting
### 6. Start application
@@ -104,82 +93,6 @@ To make sure you didn't miss anything run a more thorough check with:
If all items are green, then congratulations upgrade is complete!
-
-### 8. Optional optimizations for GitLab setups with MySQL databases
-
-Only applies if running MySQL database created with GitLab 6.7 or earlier. If you are not experiencing any issues you may not need the following instructions however following them will bring your database in line with the latest recommended installation configuration and help avoid future issues. Be sure to follow these directions exactly. These directions should be safe for any MySQL instance but to be sure make a current MySQL database backup beforehand.
-
-```
-# Stop GitLab
-sudo service gitlab stop
-
-# Secure your MySQL installation (added in GitLab 6.2)
-sudo mysql_secure_installation
-
-# Login to MySQL
-mysql -u root -p
-
-# do not type the 'mysql>', this is part of the prompt
-
-# Convert all tables to use the InnoDB storage engine (added in GitLab 6.8)
-SELECT CONCAT('ALTER TABLE gitlabhq_production.', table_name, ' ENGINE=InnoDB;') AS 'Copy & run these SQL statements:' FROM information_schema.tables WHERE table_schema = 'gitlabhq_production' AND `ENGINE` <> 'InnoDB' AND `TABLE_TYPE` = 'BASE TABLE';
-
-# If previous query returned results, copy & run all outputed SQL statements
-
-# Convert all tables to correct character set
-SET foreign_key_checks = 0;
-SELECT CONCAT('ALTER TABLE gitlabhq_production.', table_name, ' CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;') AS 'Copy & run these SQL statements:' FROM information_schema.tables WHERE table_schema = 'gitlabhq_production' AND `TABLE_COLLATION` <> 'utf8_unicode_ci' AND `TABLE_TYPE` = 'BASE TABLE';
-
-# If previous query returned results, copy & run all outputed SQL statements
-
-# turn foreign key checks back on
-SET foreign_key_checks = 1;
-
-# Find MySQL users
-mysql> SELECT user FROM mysql.user WHERE user LIKE '%git%';
-
-# If git user exists and gitlab user does not exist
-# you are done with the database cleanup tasks
-mysql> \q
-
-# If both users exist skip to Delete gitlab user
-
-# Create new user for GitLab (changed in GitLab 6.4)
-# change $password in the command below to a real password you pick
-mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
-
-# Grant the git user necessary permissions on the database
-mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON `gitlabhq_production`.* TO 'git'@'localhost';
-
-# Delete the old gitlab user
-mysql> DELETE FROM mysql.user WHERE user='gitlab';
-
-# Quit the database session
-mysql> \q
-
-# Try connecting to the new database with the new user
-sudo -u git -H mysql -u git -p -D gitlabhq_production
-
-# Type the password you replaced $password with earlier
-
-# You should now see a 'mysql>' prompt
-
-# Quit the database session
-mysql> \q
-
-# Update database configuration details
-# See config/database.yml.mysql for latest recommended configuration details
-# Remove the reaping_frequency setting line if it exists (removed in GitLab 6.8)
-# Set production -> pool: 10 (updated in GitLab 5.3)
-# Set production -> username: git
-# Set production -> password: the password your replaced $password with earlier
-sudo -u git -H editor /home/git/gitlab/config/database.yml
-
-# Run thorough check
-sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
-```
-
-
## Things went south? Revert to previous version (7.4)
### 1. Revert the code to the previous version
diff --git a/doc/update/7.5-to-7.6.md b/doc/update/7.5-to-7.6.md
new file mode 100644
index 00000000000..a5d76c341af
--- /dev/null
+++ b/doc/update/7.5-to-7.6.md
@@ -0,0 +1,116 @@
+# From 7.5 to 7.6
+
+**7.6 is not yet released. This is a preliminary upgrade guide.**
+
+### 0. Stop server
+
+ sudo service gitlab stop
+
+### 1. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 2. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 7-6-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 7-6-stable-ee
+```
+
+### 3. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v2.2.0
+```
+
+### 4. Install libs, migrations, etc.
+
+```bash
+sudo apt-get install libkrb5-dev
+
+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
+```
+
+### 5. Update config files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`.
+
+```
+git diff origin/7-5-stable:config/gitlab.yml.example origin/7-6-stable:config/gitlab.yml.example
+```
+
+#### Change Nginx settings
+
+* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings
+* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your setting
+
+#### Setup time zone (optional)
+
+Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`](config/application.rb) (unlikely), unset it.
+
+### 6. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 7. 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 (7.5)
+
+### 1. Revert the code to the previous version
+Follow the [upgrade guide from 7.4 to 7.5](7.4-to-7.5.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.
diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md
index 44e18a9ed42..0a9f242d9ab 100644
--- a/doc/update/upgrader.md
+++ b/doc/update/upgrader.md
@@ -10,6 +10,8 @@ If you have local changes to your GitLab repository the script will stash them a
**GitLab Upgrader is available only for GitLab version 6.4.2 or higher.**
+**This script does NOT update gitlab-shell, it needs manual update. See step 5 below.**
+
## 0. Backup
cd /home/git/gitlab
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index f19517c0f18..e17d21b990d 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -54,6 +54,29 @@ Triggered when you push to the repository except when pushing tags.
}
```
+## Tag events
+
+Triggered when you create (or delete) tags to the repository.
+
+**Request body:**
+
+```json
+{
+ "ref": "refs/tags/v1.0.0",
+ "before": "0000000000000000000000000000000000000000",
+ "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7",
+ "user_id": 1,
+ "user_name": "John Smith",
+ "project_id": 1,
+ "repository": {
+ "name": "jsmith",
+ "url": "ssh://git@example.com/jsmith/example.git",
+ "description": "",
+ "homepage": "http://example.com/jsmith/example"
+ }
+}
+```
+
## Issues events
Triggered when a new issue is created or an existing issue was updated/closed/reopened.
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 00000000000..aea59916c7a
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,34 @@
+FROM ubuntu:14.04
+
+# Install required packages
+RUN apt-get update -q \
+ && DEBIAN_FRONTEND=noninteractive apt-get install -qy \
+ openssh-server \
+ wget \
+ && apt-get clean
+
+# Download & Install GitLab
+# If the Omnibus package version below is outdated please contribute a merge request to update it.
+# If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
+RUN TMP_FILE=$(mktemp); \
+ wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.5.2-omnibus.5.2.1.ci-1_amd64.deb \
+ && dpkg -i $TMP_FILE \
+ && rm -f $TMP_FILE
+
+# Manage SSHD through runit
+RUN mkdir -p /opt/gitlab/sv/sshd/supervise \
+ && mkfifo /opt/gitlab/sv/sshd/supervise/ok \
+ && printf "#!/bin/sh\nexec 2>&1\numask 077\nexec /usr/sbin/sshd -D" > /opt/gitlab/sv/sshd/run \
+ && chmod a+x /opt/gitlab/sv/sshd/run \
+ && ln -s /opt/gitlab/sv/sshd /opt/gitlab/service \
+ && mkdir -p /var/run/sshd
+
+# Expose web & ssh
+EXPOSE 80 22
+
+# Volume & configuration
+VOLUME ["/var/opt/gitlab", "/var/log/gitlab", "/etc/gitlab"]
+ADD gitlab.rb /etc/gitlab/
+
+# Default is to run runit & reconfigure
+CMD gitlab-ctl reconfigure & /opt/gitlab/embedded/bin/runsvdir-start
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 00000000000..58982a238a8
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,68 @@
+What is GitLab?
+===============
+
+GitLab offers git repository management, code reviews, issue tracking, activity feeds, wikis. It has LDAP/AD integration, handles 25,000 users on a single server but can also run on a highly available active/active cluster. A subscription gives you access to our support team and to GitLab Enterprise Edition that contains extra features aimed at larger organizations.
+
+<https://about.gitlab.com>
+
+![GitLab Logo](https://gitlab.com/uploads/appearance/logo/1/brand_logo-c37eb221b456bb4b472cc1084480991f.png)
+
+
+How to use this image
+======================
+
+At this moment GitLab doesn't have official Docker images.
+Build your own based on the Omnibus packages with the following command (it assumes you're in the GitLab repo root directory):
+
+```bash
+sudo docker build --tag gitlab_image docker/
+```
+
+We assume using a data volume container, this will simplify migrations and backups.
+This empty container will exist to persist as volumes the 3 directories used by GitLab, so remember not to delete it.
+
+The directories on data container are:
+
+- `/var/opt/gitlab` for application data
+- `/var/log/gitlab` for logs
+- `/etc/gitlab` for configuration
+
+Create the data container with:
+
+```bash
+sudo docker run --name gitlab_data gitlab_image /bin/true
+```
+
+After creating this run GitLab:
+
+```bash
+sudo docker run --detach --name gitlab_app --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_image
+```
+
+It might take a while before the docker container is responding to queries. You can follow the configuration process with `docker logs -f gitlab_app`.
+
+You can then go to `http://localhost:8080/` (or `http://192.168.59.103:8080/` if you use boot2docker).
+You can login with username `root` and password `5iveL!fe`.
+Next time, you can just use `sudo docker start gitlab_app` and `sudo docker stop gitlab_app`.
+
+
+How to configure GitLab
+========================
+
+This container uses the official Omnibus GitLab distribution, so all configuration is done in the unique configuration file `/etc/gitlab/gitlab.rb`.
+
+To access GitLab configuration, you can start an interactive command line in a new container using the shared data volume container, you will be able to browse the 3 directories and use your favorite text editor:
+
+```bash
+docker run -ti -e TERM=linux --rm --volumes-from gitlab_data ubuntu
+vi /etc/gitlab/gitlab.rb
+```
+
+**Note** that GitLab will reconfigure itself **at each container start.** You will need to restart the container to reconfigure your GitLab.
+
+You can find all available options in [Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#configuration).
+
+
+Troubleshooting
+=========================
+Please see the [troubleshooting](troubleshooting.md) file in this directory.
diff --git a/docker/gitlab.rb b/docker/gitlab.rb
new file mode 100644
index 00000000000..7fddf309c01
--- /dev/null
+++ b/docker/gitlab.rb
@@ -0,0 +1,37 @@
+# External URL should be your Docker instance.
+# By default, this example is the "standard" boot2docker IP.
+# Always use port 80 here to force the internal nginx to bind port 80,
+# even if you intend to use another port in Docker.
+external_url "http://192.168.59.103/"
+
+# Prevent Postgres from trying to allocate 25% of total memory
+postgresql['shared_buffers'] = '1MB'
+
+# Configure GitLab to redirect PostgreSQL logs to the data volume
+postgresql['log_directory'] = '/var/log/gitlab/postgresql'
+
+# Some configuration of GitLab
+# You can find more at https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#configuration
+gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'
+gitlab_rails['gitlab_support_email'] = 'support@example.com'
+gitlab_rails['time_zone'] = 'Europe/Paris'
+
+# SMTP settings
+# You must use an external server, the Docker container does not install an SMTP server
+gitlab_rails['smtp_enable'] = true
+gitlab_rails['smtp_address'] = "smtp.example.com"
+gitlab_rails['smtp_port'] = 587
+gitlab_rails['smtp_user_name'] = "user"
+gitlab_rails['smtp_password'] = "password"
+gitlab_rails['smtp_domain'] = "example.com"
+gitlab_rails['smtp_authentication'] = "plain"
+gitlab_rails['smtp_enable_starttls_auto'] = true
+
+# Enable LDAP authentication
+# gitlab_rails['ldap_enabled'] = true
+# gitlab_rails['ldap_host'] = 'ldap.example.com'
+# gitlab_rails['ldap_port'] = 389
+# gitlab_rails['ldap_method'] = 'plain' # 'ssl' or 'plain'
+# gitlab_rails['ldap_allow_username_or_email_login'] = false
+# gitlab_rails['ldap_uid'] = 'uid'
+# gitlab_rails['ldap_base'] = 'ou=users,dc=example,dc=com'
diff --git a/docker/troubleshooting.md b/docker/troubleshooting.md
new file mode 100644
index 00000000000..b1b70de5997
--- /dev/null
+++ b/docker/troubleshooting.md
@@ -0,0 +1,63 @@
+# Troubleshooting
+
+This is to troubleshoot https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/245
+But it might contain useful commands for other cases as well.
+
+The configuration to add the postgres log in vim is:
+postgresql['log_directory'] = '/var/log/gitlab/postgresql'
+
+# Commands
+
+```bash
+sudo docker build --tag gitlab_image docker/
+
+sudo docker rm -f gitlab_app
+sudo docker rm -f gitlab_data
+
+sudo docker run --name gitlab_data gitlab_image /bin/true
+
+sudo docker run -ti --rm --volumes-from gitlab_data ubuntu apt-get update && sudo apt-get install -y vim && sudo vim /etc/gitlab/gitlab.rb
+
+sudo docker run --detach --name gitlab_app --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_image
+
+sudo docker run -t --rm --volumes-from gitlab_data ubuntu tail -f /var/log/gitlab/reconfigure.log
+
+sudo docker run -t --rm --volumes-from gitlab_data ubuntu tail -f /var/log/gitlab/postgresql/current
+
+sudo docker run -t --rm --volumes-from gitlab_data ubuntu cat /var/opt/gitlab/postgresql/data/postgresql.conf | grep shared_buffers
+
+sudo docker run -t --rm --volumes-from gitlab_data ubuntu cat /etc/gitlab/gitlab.rb
+```
+
+# Interactively
+
+```bash
+# First start a GitLab container without starting GitLab
+# This is almost the same as starting the GitLab container except:
+# - we run interactively (-t -i)
+# - we define TERM=linux because it allows to use arrow keys in vi (!!!)
+# - we choose another startup command (bash)
+sudo docker run -ti -e TERM=linux --name gitlab_app --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab_image bash
+
+# Configure GitLab to redirect PostgreSQL logs
+echo "postgresql['log_directory'] = '/var/log/gitlab/postgresql'" >> /etc/gitlab/gitlab.rb
+
+# Prevent Postgres from allocating 25% of total memory
+echo "postgresql['shared_buffers'] = '1MB'" >> /etc/gitlab/gitlab.rb
+
+# You can now start GitLab manually from Bash (in the background)
+# Maybe the command below is still missing something to run in the background
+gitlab-ctl reconfigure > /var/log/gitlab/reconfigure.log & /opt/gitlab/embedded/bin/runsvdir-start &
+
+# Inspect PostgreSQL config
+cat /var/opt/gitlab/postgresql/data/postgresql.conf | grep shared_buffers
+
+# And tail the logs (PostgreSQL log may not exist immediately)
+tail -f /var/log/gitlab/reconfigure.log /var/log/gitlab/postgresql/current
+
+# And get the memory
+cat /proc/meminfo
+head /proc/sys/kernel/shmmax /proc/sys/kernel/shmall
+free -m
+
+```
diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature
index 8d3e0bd967f..ed548177837 100644
--- a/features/project/active_tab.feature
+++ b/features/project/active_tab.feature
@@ -110,7 +110,7 @@ Feature: Project Active Tab
Scenario: On Project Issues/Browse
Given I visit my project's issues page
- Then the active sub tab should be Browse Issues
+ Then the active sub tab should be Issues
And no other sub tabs should be active
And the active main tab should be Issues
diff --git a/features/project/commits/comments.feature b/features/project/commits/comments.feature
index e176752cfbf..a45245917e3 100644
--- a/features/project/commits/comments.feature
+++ b/features/project/commits/comments.feature
@@ -16,12 +16,12 @@ Feature: Project Commits Comments
@javascript
Scenario: I can't preview without text
Given I haven't written any comment text
- Then I should not see the comment preview button
+ Then The comment preview tab should say there is nothing to do
@javascript
Scenario: I can preview with text
- Given I write a comment like "Nice"
- Then I should see the comment preview button
+ Given I write a comment like ":+1: Nice"
+ Then The comment preview tab should be display rendered Markdown
@javascript
Scenario: I preview a comment
@@ -32,7 +32,7 @@ Feature: Project Commits Comments
@javascript
Scenario: I can edit after preview
Given I preview a comment text like "Bug fixed :smile:"
- Then I should see the comment edit button
+ Then I should see the comment write tab
@javascript
Scenario: I have a reset form after posting from preview
diff --git a/features/project/commits/diff_comments.feature b/features/project/commits/diff_comments.feature
index a145ec84b78..9c4cc723d1b 100644
--- a/features/project/commits/diff_comments.feature
+++ b/features/project/commits/diff_comments.feature
@@ -58,13 +58,13 @@ Feature: Project Commits Diff Comments
Scenario: I can't preview without text
Given I open a diff comment form
And I haven't written any diff comment text
- Then I should not see the diff comment preview button
+ Then The diff comment preview tab should say there is nothing to do
@javascript
Scenario: I can preview with text
Given I open a diff comment form
And I write a diff comment like ":-1: I don't like this"
- Then I should see the diff comment preview button
+ Then The diff comment preview tab should display rendered Markdown
@javascript
Scenario: I preview a diff comment
@@ -75,7 +75,7 @@ Feature: Project Commits Diff Comments
@javascript
Scenario: I can edit after preview
Given I preview a diff comment text like "Should fix it :smile:"
- Then I should see the diff comment edit button
+ Then I should see the diff comment write tab
@javascript
Scenario: The form gets removed after posting
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 4db8551559b..28ea44530fe 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -159,3 +159,37 @@ Feature: Project Issues
Given project "Shop" has "Tasks-closed" closed issue with task markdown
When I visit issue page "Tasks-closed"
Then Task checkboxes should be disabled
+
+ # Issue description preview
+
+ @javascript
+ Scenario: I can't preview without text
+ Given I click link "New Issue"
+ And I haven't written any description text
+ Then The Markdown preview tab should say there is nothing to do
+
+ @javascript
+ Scenario: I can preview with text
+ Given I click link "New Issue"
+ And I write a description like ":+1: Nice"
+ Then The Markdown preview tab should display rendered Markdown
+
+ @javascript
+ Scenario: I preview an issue description
+ Given I click link "New Issue"
+ And I preview a description text like "Bug fixed :smile:"
+ Then I should see the Markdown preview
+ And I should not see the Markdown text field
+
+ @javascript
+ Scenario: I can edit after preview
+ Given I click link "New Issue"
+ And I preview a description text like "Bug fixed :smile:"
+ Then I should see the Markdown write tab
+
+ @javascript
+ Scenario: I can preview when editing an existing issue
+ Given I click link "Release 0.4"
+ And I click link "Edit" for the issue
+ And I preview a description text like "Bug fixed :smile:"
+ Then I should see the Markdown write tab
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index d20358a7dc6..7c029f05d75 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -187,3 +187,34 @@ Feature: Project Merge Requests
And I visit merge request page "MR-task-open"
And I click link "Close"
Then Task checkboxes should be disabled
+
+ # Description preview
+
+ @javascript
+ Scenario: I can't preview without text
+ Given I visit merge request page "Bug NS-04"
+ And I click link "Edit" for the merge request
+ And I haven't written any description text
+ Then The Markdown preview tab should say there is nothing to do
+
+ @javascript
+ Scenario: I can preview with text
+ Given I visit merge request page "Bug NS-04"
+ And I click link "Edit" for the merge request
+ And I write a description like ":+1: Nice"
+ Then The Markdown preview tab should display rendered Markdown
+
+ @javascript
+ Scenario: I preview a merge request description
+ Given I visit merge request page "Bug NS-04"
+ And I click link "Edit" for the merge request
+ And I preview a description text like "Bug fixed :smile:"
+ Then I should see the Markdown preview
+ And I should not see the Markdown text field
+
+ @javascript
+ Scenario: I can edit after preview
+ Given I visit merge request page "Bug NS-04"
+ And I click link "Edit" for the merge request
+ And I preview a description text like "Bug fixed :smile:"
+ Then I should see the Markdown write tab
diff --git a/features/project/service.feature b/features/project/service.feature
index 88fd038d45f..ed9e03b428d 100644
--- a/features/project/service.feature
+++ b/features/project/service.feature
@@ -19,6 +19,12 @@ Feature: Project Services
And I fill hipchat settings
Then I should see hipchat service settings saved
+ Scenario: Activate hipchat service with custom server
+ When I visit project "Shop" services page
+ And I click hipchat service link
+ And I fill hipchat settings with custom server
+ Then I should see hipchat service settings with custom server saved
+
Scenario: Activate pivotaltracker service
When I visit project "Shop" services page
And I click pivotaltracker service link
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 6d747b65bae..38aaadcd28d 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -170,7 +170,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step "I am not an ldap user" do
- current_user.update_attributes(extern_uid: nil, provider: '')
+ current_user.identities.delete
current_user.ldap_user?.should be_false
end
diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb
index 83796b0ba88..bb42d15eae5 100644
--- a/features/steps/project/active_tab.rb
+++ b/features/steps/project/active_tab.rb
@@ -89,8 +89,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
click_link('Labels')
end
- step 'the active sub tab should be Browse Issues' do
- ensure_active_sub_tab('Browse Issues')
+ step 'the active sub tab should be Issues' do
+ ensure_active_sub_tab('Issues')
end
step 'the active sub tab should be Milestones' do
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index 640603562dd..c0ae5208541 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -1,5 +1,6 @@
class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
include SharedAuthentication
+ include SharedIssuable
include SharedProject
include SharedNote
include SharedPaths
diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb
index 89d7af3c9ee..cce87a6d981 100644
--- a/features/steps/project/issues/milestones.rb
+++ b/features/steps/project/issues/milestones.rb
@@ -8,7 +8,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
milestone = @project.milestones.find_by(title: "v2.2")
page.should have_content(milestone.title[0..10])
page.should have_content(milestone.expires_at)
- page.should have_content("Browse Issues")
+ page.should have_content("Issues")
end
step 'I click link "v2.2"' do
@@ -28,7 +28,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
milestone = @project.milestones.find_by(title: "v2.3")
page.should have_content(milestone.title[0..10])
page.should have_content(milestone.expires_at)
- page.should have_content("Browse Issues")
+ page.should have_content("Issues")
end
step 'project "Shop" has milestone "v2.2"' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index fae0cec53a6..d5e060bdbe8 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -1,5 +1,6 @@
class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
include SharedAuthentication
+ include SharedIssuable
include SharedProject
include SharedNote
include SharedPaths
diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb
index d5d58070d86..7a0b47a8fe5 100644
--- a/features/steps/project/services.rb
+++ b/features/steps/project/services.rb
@@ -10,7 +10,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
step 'I should see list of available services' do
page.should have_content 'Project services'
page.should have_content 'Campfire'
- page.should have_content 'Hipchat'
+ page.should have_content 'HipChat'
page.should have_content 'GitLab CI'
page.should have_content 'Assembla'
page.should have_content 'Pushover'
@@ -33,7 +33,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I click hipchat service link' do
- click_link 'Hipchat'
+ click_link 'HipChat'
end
step 'I fill hipchat settings' do
@@ -47,6 +47,17 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
find_field('Room').value.should == 'gitlab'
end
+ step 'I fill hipchat settings with custom server' do
+ check 'Active'
+ fill_in 'Room', with: 'gitlab_custom'
+ fill_in 'Token', with: 'secretCustom'
+ fill_in 'Server', with: 'https://chat.example.com'
+ click_button 'Save'
+ end
+
+ step 'I should see hipchat service settings with custom server saved' do
+ find_field('Server').value.should == 'https://chat.example.com'
+ end
step 'I click pivotaltracker service link' do
click_link 'PivotalTracker'
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 10f3ed90b56..28964d54a8f 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -32,7 +32,7 @@ module SharedDiffNote
click_diff_line(sample_commit.line_code)
within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "Should fix it :smile:"
- find(".js-note-preview-button").trigger("click")
+ find('.js-md-preview-button').click
end
end
@@ -41,7 +41,7 @@ module SharedDiffNote
within("#{diff_file_selector} form[rel$='#{sample_commit.del_line_code}']") do
fill_in "note[note]", with: "DRY this up"
- find(".js-note-preview-button").trigger("click")
+ find('.js-md-preview-button').click
end
end
@@ -71,9 +71,10 @@ module SharedDiffNote
end
end
- step 'I should not see the diff comment preview button' do
+ step 'The diff comment preview tab should say there is nothing to do' do
within(diff_file_selector) do
- page.should have_css(".js-note-preview-button", visible: false)
+ find('.js-md-preview-button').click
+ expect(find('.js-md-preview')).to have_content('Nothing to preview.')
end
end
@@ -131,27 +132,28 @@ module SharedDiffNote
step 'I should see the diff comment preview' do
within("#{diff_file_selector} form") do
- page.should have_css(".js-note-preview", visible: false)
+ expect(page).to have_css('.js-md-preview', visible: true)
end
end
- step 'I should see the diff comment edit button' do
+ step 'I should see the diff comment write tab' do
within(diff_file_selector) do
- page.should have_css(".js-note-write-button", visible: true)
+ expect(page).to have_css('.js-md-write-button', visible: true)
end
end
- step 'I should see the diff comment preview button' do
+ step 'The diff comment preview tab should display rendered Markdown' do
within(diff_file_selector) do
- page.should have_css(".js-note-preview-button", visible: true)
+ find('.js-md-preview-button').click
+ expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
end
end
step 'I should see two separate previews' do
within(diff_file_selector) do
- page.should have_css(".js-note-preview", visible: true, count: 2)
- page.should have_content("Should fix it")
- page.should have_content("DRY this up")
+ expect(page).to have_css('.js-md-preview', visible: true, count: 2)
+ expect(page).to have_content('Should fix it')
+ expect(page).to have_content('DRY this up')
end
end
diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb
new file mode 100644
index 00000000000..a0150e90380
--- /dev/null
+++ b/features/steps/shared/issuable.rb
@@ -0,0 +1,15 @@
+module SharedIssuable
+ include Spinach::DSL
+
+ def edit_issuable
+ find('.issue-btn-group').click_link 'Edit'
+ end
+
+ step 'I click link "Edit" for the merge request' do
+ edit_issuable
+ end
+
+ step 'I click link "Edit" for the issue' do
+ edit_issuable
+ end
+end
diff --git a/features/steps/shared/markdown.rb b/features/steps/shared/markdown.rb
index 8bf138065b0..e71700880cd 100644
--- a/features/steps/shared/markdown.rb
+++ b/features/steps/shared/markdown.rb
@@ -54,4 +54,49 @@ EOT
'div.description li.task-list-item input[type="checkbox"]:disabled'
)
end
+
+ step 'I should not see the Markdown preview' do
+ expect(find('.gfm-form .js-md-preview')).not_to be_visible
+ end
+
+ step 'The Markdown preview tab should say there is nothing to do' do
+ within('.gfm-form') do
+ find('.js-md-preview-button').click
+ expect(find('.js-md-preview')).to have_content('Nothing to preview.')
+ end
+ end
+
+ step 'I should not see the Markdown text field' do
+ expect(find('.gfm-form textarea')).not_to be_visible
+ end
+
+ step 'I should see the Markdown write tab' do
+ expect(find('.gfm-form')).to have_css('.js-md-write-button', visible: true)
+ end
+
+ step 'I should see the Markdown preview' do
+ expect(find('.gfm-form')).to have_css('.js-md-preview', visible: true)
+ end
+
+ step 'The Markdown preview tab should display rendered Markdown' do
+ within('.gfm-form') do
+ find('.js-md-preview-button').click
+ expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
+ end
+ end
+
+ step 'I write a description like ":+1: Nice"' do
+ find('.gfm-form').fill_in 'Description', with: ':+1: Nice'
+ end
+
+ step 'I preview a description text like "Bug fixed :smile:"' do
+ within('.gfm-form') do
+ fill_in 'Description', with: 'Bug fixed :smile:'
+ find('.js-md-preview-button').click
+ end
+ end
+
+ step 'I haven\'t written any description text' do
+ find('.gfm-form').fill_in 'Description', with: ''
+ end
end
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index 2b2cb47a715..17adec3eda1 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -23,7 +23,7 @@ module SharedNote
step 'I preview a comment text like "Bug fixed :smile:"' do
within(".js-main-target-form") do
fill_in "note[note]", with: "Bug fixed :smile:"
- find(".js-note-preview-button").trigger("click")
+ find('.js-md-preview-button').click
end
end
@@ -33,9 +33,9 @@ module SharedNote
end
end
- step 'I write a comment like "Nice"' do
+ step 'I write a comment like ":+1: Nice"' do
within(".js-main-target-form") do
- fill_in "note[note]", with: "Nice"
+ fill_in 'note[note]', with: ':+1: Nice'
end
end
@@ -51,13 +51,14 @@ module SharedNote
step 'I should not see the comment preview' do
within(".js-main-target-form") do
- page.should have_css(".js-note-preview", visible: false)
+ expect(find('.js-md-preview')).not_to be_visible
end
end
- step 'I should not see the comment preview button' do
+ step 'The comment preview tab should say there is nothing to do' do
within(".js-main-target-form") do
- page.should have_css(".js-note-preview-button", visible: false)
+ find('.js-md-preview-button').click
+ expect(find('.js-md-preview')).to have_content('Nothing to preview.')
end
end
@@ -79,21 +80,22 @@ module SharedNote
end
end
- step 'I should see the comment edit button' do
+ step 'I should see the comment write tab' do
within(".js-main-target-form") do
- page.should have_css(".js-note-write-button", visible: true)
+ expect(page).to have_css('.js-md-write-button', visible: true)
end
end
- step 'I should see the comment preview' do
+ step 'The comment preview tab should be display rendered Markdown' do
within(".js-main-target-form") do
- page.should have_css(".js-note-preview", visible: true)
+ find('.js-md-preview-button').click
+ expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
end
end
- step 'I should see the comment preview button' do
+ step 'I should see the comment preview' do
within(".js-main-target-form") do
- page.should have_css(".js-note-preview-button", visible: true)
+ expect(page).to have_css('.js-md-preview', visible: true)
end
end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index bd7e6e1d8b3..0bd5653538c 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -131,7 +131,7 @@ module SharedProject
end
step 'public empty project "Empty Public Project"' do
- create :empty_project, :public, name: "Empty Public Project"
+ create :project_empty_repo, :public, name: "Empty Public Project"
end
step 'project "Community" has comments' do
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 42e4442365d..2fea151aeb3 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -14,10 +14,14 @@ module API
expose :bio, :skype, :linkedin, :twitter, :website_url
end
+ class Identity < Grape::Entity
+ expose :provider, :extern_uid
+ end
+
class UserFull < User
expose :email
- expose :theme_id, :color_scheme_id, :extern_uid, :provider, \
- :projects_limit
+ expose :theme_id, :color_scheme_id, :projects_limit
+ expose :identities, using: Entities::Identity
expose :can_create_group?, as: :can_create_group
expose :can_create_project?, as: :can_create_project
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index ebf2296097d..180e50611cf 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -33,17 +33,22 @@ module API
end
project = Project.find_with_namespace(project_path)
- return false unless project
+
+ unless project
+ return Gitlab::GitAccessStatus.new(false, 'No such project')
+ end
actor = if params[:key_id]
- Key.find(params[:key_id])
+ Key.find_by(id: params[:key_id])
elsif params[:user_id]
- User.find(params[:user_id])
+ User.find_by(id: params[:user_id])
end
- return false unless actor
+ unless actor
+ return Gitlab::GitAccessStatus.new(false, 'No such user or key')
+ end
- access.allowed?(
+ access.check(
actor,
params[:action],
project,
diff --git a/lib/api/users.rb b/lib/api/users.rb
index d07815a8a97..37b36ddcf94 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -59,10 +59,16 @@ module API
post do
authenticated_as_admin!
required_attributes! [:email, :password, :name, :username]
- attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio, :can_create_group, :admin]
+ attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :can_create_group, :admin]
user = User.build_user(attrs)
admin = attrs.delete(:admin)
user.admin = admin unless admin.nil?
+
+ identity_attrs = attributes_for_keys [:provider, :extern_uid]
+ if identity_attrs.any?
+ user.identities.build(identity_attrs)
+ end
+
if user.save
present user, with: Entities::UserFull
else
@@ -89,8 +95,6 @@ module API
# twitter - Twitter account
# website_url - Website url
# projects_limit - Limit projects each user can create
- # extern_uid - External authentication provider UID
- # provider - External provider
# bio - Bio
# admin - User is admin - true or false (default)
# can_create_group - User can create groups - true or false
@@ -99,7 +103,7 @@ module API
put ":id" do
authenticated_as_admin!
- attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :extern_uid, :provider, :bio, :can_create_group, :admin]
+ attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :bio, :can_create_group, :admin]
user = User.find(params[:id])
not_found!('User') unless user
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index ea659e3b605..9ab6aca276d 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -13,10 +13,10 @@ module Backup
def dump
success = case config["adapter"]
when /^mysql/ then
- print "Dumping MySQL database #{config['database']} ... "
+ $progress.print "Dumping MySQL database #{config['database']} ... "
system('mysqldump', *mysql_args, config['database'], out: db_file_name)
when "postgresql" then
- print "Dumping PostgreSQL database #{config['database']} ... "
+ $progress.print "Dumping PostgreSQL database #{config['database']} ... "
pg_env
system('pg_dump', config['database'], out: db_file_name)
end
@@ -27,10 +27,10 @@ module Backup
def restore
success = case config["adapter"]
when /^mysql/ then
- print "Restoring MySQL database #{config['database']} ... "
+ $progress.print "Restoring MySQL database #{config['database']} ... "
system('mysql', *mysql_args, config['database'], in: db_file_name)
when "postgresql" then
- print "Restoring PostgreSQL database #{config['database']} ... "
+ $progress.print "Restoring PostgreSQL database #{config['database']} ... "
# Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE
# statements like MySQL.
Rake::Task["gitlab:db:drop_all_tables"].invoke
@@ -69,9 +69,9 @@ module Backup
def report_success(success)
if success
- puts '[DONE]'.green
+ $progress.puts '[DONE]'.green
else
- puts '[FAILED]'.red
+ $progress.puts '[FAILED]'.red
end
end
end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 03fe0f0b02f..ab8db4e9837 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -18,11 +18,11 @@ module Backup
end
# create archive
- print "Creating backup archive: #{tar_file} ... "
+ $progress.print "Creating backup archive: #{tar_file} ... "
if Kernel.system('tar', '-cf', tar_file, *BACKUP_CONTENTS)
- puts "done".green
+ $progress.puts "done".green
else
- puts "failed".red
+ puts "creating archive #{tar_file} failed".red
abort 'Backup failed'
end
@@ -31,37 +31,37 @@ module Backup
def upload(tar_file)
remote_directory = Gitlab.config.backup.upload.remote_directory
- print "Uploading backup archive to remote storage #{remote_directory} ... "
+ $progress.print "Uploading backup archive to remote storage #{remote_directory} ... "
connection_settings = Gitlab.config.backup.upload.connection
if connection_settings.blank?
- puts "skipped".yellow
+ $progress.puts "skipped".yellow
return
end
connection = ::Fog::Storage.new(connection_settings)
directory = connection.directories.get(remote_directory)
if directory.files.create(key: tar_file, body: File.open(tar_file), public: false)
- puts "done".green
+ $progress.puts "done".green
else
- puts "failed".red
+ puts "uploading backup to #{remote_directory} failed".red
abort 'Backup failed'
end
end
def cleanup
- print "Deleting tmp directories ... "
+ $progress.print "Deleting tmp directories ... "
if Kernel.system('rm', '-rf', *BACKUP_CONTENTS)
- puts "done".green
+ $progress.puts "done".green
else
- puts "failed".red
+ puts "deleting tmp directory failed".red
abort 'Backup failed'
end
end
def remove_old
# delete backups
- print "Deleting old backups ... "
+ $progress.print "Deleting old backups ... "
keep_time = Gitlab.config.backup.keep_time.to_i
path = Gitlab.config.backup.path
@@ -76,9 +76,9 @@ module Backup
end
end
end
- puts "done. (#{removed} removed)".green
+ $progress.puts "done. (#{removed} removed)".green
else
- puts "skipping".yellow
+ $progress.puts "skipping".yellow
end
end
@@ -101,12 +101,12 @@ module Backup
exit 1
end
- print "Unpacking backup ... "
+ $progress.print "Unpacking backup ... "
unless Kernel.system(*%W(tar -xf #{tar_file}))
- puts "failed".red
+ puts "unpacking backup failed".red
exit 1
else
- puts "done".green
+ $progress.puts "done".green
end
settings = YAML.load_file("backup_information.yml")
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index faa1b3b4099..e18bc804437 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -8,19 +8,21 @@ module Backup
prepare
Project.find_each(batch_size: 1000) do |project|
- print " * #{project.path_with_namespace} ... "
+ $progress.print " * #{project.path_with_namespace} ... "
# Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
if project.empty_repo?
- puts "[SKIPPED]".cyan
+ $progress.puts "[SKIPPED]".cyan
else
- output, status = Gitlab::Popen.popen(%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all))
+ cmd = %W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all)
+ output, status = Gitlab::Popen.popen(cmd)
if status.zero?
- puts "[DONE]".green
+ $progress.puts "[DONE]".green
else
puts "[FAILED]".red
+ puts "failed: #{cmd.join(' ')}"
puts output
abort 'Backup failed'
end
@@ -29,15 +31,17 @@ module Backup
wiki = ProjectWiki.new(project)
if File.exists?(path_to_repo(wiki))
- print " * #{wiki.path_with_namespace} ... "
+ $progress.print " * #{wiki.path_with_namespace} ... "
if wiki.repository.empty?
- puts " [SKIPPED]".cyan
+ $progress.puts " [SKIPPED]".cyan
else
- output, status = Gitlab::Popen.popen(%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all))
+ cmd = %W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all)
+ output, status = Gitlab::Popen.popen(cmd)
if status.zero?
- puts " [DONE]".green
+ $progress.puts " [DONE]".green
else
puts " [FAILED]".red
+ puts "failed: #{cmd.join(' ')}"
abort 'Backup failed'
end
end
@@ -55,7 +59,7 @@ module Backup
FileUtils.mkdir_p(repos_path)
Project.find_each(batch_size: 1000) do |project|
- print "#{project.path_with_namespace} ... "
+ $progress.print " * #{project.path_with_namespace} ... "
project.namespace.ensure_dir_exist if project.namespace
@@ -66,30 +70,41 @@ module Backup
end
if system(*cmd, silent)
- puts "[DONE]".green
+ $progress.puts "[DONE]".green
else
puts "[FAILED]".red
+ puts "failed: #{cmd.join(' ')}"
abort 'Restore failed'
end
wiki = ProjectWiki.new(project)
if File.exists?(path_to_bundle(wiki))
- print " * #{wiki.path_with_namespace} ... "
- if system(*%W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}), silent)
- puts " [DONE]".green
+ $progress.print " * #{wiki.path_with_namespace} ... "
+
+ # If a wiki bundle exists, first remove the empty repo
+ # that was initialized with ProjectWiki.new() and then
+ # try to restore with 'git clone --bare'.
+ FileUtils.rm_rf(path_to_repo(wiki))
+ cmd = %W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)})
+
+ if system(*cmd, silent)
+ $progress.puts " [DONE]".green
else
puts " [FAILED]".red
+ puts "failed: #{cmd.join(' ')}"
abort 'Restore failed'
end
end
end
- print 'Put GitLab hooks in repositories dirs'.yellow
- if system("#{Gitlab.config.gitlab_shell.path}/bin/create-hooks")
- puts " [DONE]".green
+ $progress.print 'Put GitLab hooks in repositories dirs'.yellow
+ cmd = "#{Gitlab.config.gitlab_shell.path}/bin/create-hooks"
+ if system(cmd)
+ $progress.puts " [DONE]".green
else
puts " [FAILED]".red
+ puts "failed: #{cmd}"
end
end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index df1461a45c9..762639414e0 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -80,7 +80,7 @@ module Grack
case git_cmd
when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
if user
- Gitlab::GitAccess.new.download_allowed?(user, project)
+ Gitlab::GitAccess.new.download_access_check(user, project).allowed?
elsif project.public?
# Allow clone/fetch for public projects
true
diff --git a/lib/gitlab/force_push_check.rb b/lib/gitlab/force_push_check.rb
new file mode 100644
index 00000000000..6a52cdba608
--- /dev/null
+++ b/lib/gitlab/force_push_check.rb
@@ -0,0 +1,15 @@
+module Gitlab
+ class ForcePushCheck
+ def self.force_push?(project, oldrev, newrev)
+ return false if project.empty_repo?
+
+ if oldrev != Gitlab::Git::BLANK_SHA && newrev != Gitlab::Git::BLANK_SHA
+ missed_refs = IO.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})).read
+ missed_refs.split("\n").size > 0
+ else
+ false
+ end
+ end
+ end
+end
+
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 129881060d5..875f8d8b3a3 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -5,61 +5,77 @@ module Gitlab
attr_reader :params, :project, :git_cmd, :user
- def allowed?(actor, cmd, project, changes = nil)
+ def check(actor, cmd, project, changes = nil)
case cmd
when *DOWNLOAD_COMMANDS
+ download_access_check(actor, project)
+ when *PUSH_COMMANDS
if actor.is_a? User
- download_allowed?(actor, project)
+ push_access_check(actor, project, changes)
elsif actor.is_a? DeployKey
- actor.projects.include?(project)
+ return build_status_object(false, "Deploy key not allowed to push")
elsif actor.is_a? Key
- download_allowed?(actor.user, project)
+ push_access_check(actor.user, project, changes)
else
raise 'Wrong actor'
end
- when *PUSH_COMMANDS
- if actor.is_a? User
- push_allowed?(actor, project, changes)
- elsif actor.is_a? DeployKey
- # Deploy key not allowed to push
- return false
- elsif actor.is_a? Key
- push_allowed?(actor.user, project, changes)
+ else
+ return build_status_object(false, "Wrong command")
+ end
+ end
+
+ def download_access_check(actor, project)
+ if actor.is_a?(User)
+ user_download_access_check(actor, project)
+ elsif actor.is_a?(DeployKey)
+ if actor.projects.include?(project)
+ build_status_object(true)
else
- raise 'Wrong actor'
+ build_status_object(false, "Deploy key not allowed to access this project")
end
+ elsif actor.is_a? Key
+ user_download_access_check(actor.user, project)
else
- false
+ raise 'Wrong actor'
end
end
- def download_allowed?(user, project)
- if user && user_allowed?(user)
- user.can?(:download_code, project)
+ def user_download_access_check(user, project)
+ if user && user_allowed?(user) && user.can?(:download_code, project)
+ build_status_object(true)
else
- false
+ build_status_object(false, "You don't have access")
end
end
- def push_allowed?(user, project, changes)
- return false unless user && user_allowed?(user)
- return true if changes.blank?
+ def push_access_check(user, project, changes)
+ unless user && user_allowed?(user)
+ return build_status_object(false, "You don't have access")
+ end
+
+ if changes.blank?
+ return build_status_object(true)
+ end
+
+ unless project.repository.exists?
+ return build_status_object(false, "Repository does not exist")
+ end
changes = changes.lines if changes.kind_of?(String)
# Iterate over all changes to find if user allowed all of them to be applied
changes.each do |change|
- unless change_allowed?(user, project, change)
+ status = change_access_check(user, project, change)
+ unless status.allowed?
# If user does not have access to make at least one change - cancel all push
- return false
+ return status
end
end
- # If user has access to make all changes
- true
+ return build_status_object(true)
end
- def change_allowed?(user, project, change)
+ def change_access_check(user, project, change)
oldrev, newrev, ref = change.split(' ')
action = if project.protected_branch?(branch_name(ref))
@@ -72,25 +88,22 @@ module Gitlab
else
:push_code_to_protected_branches
end
- elsif project.repository && project.repository.tag_names.include?(tag_name(ref))
+ elsif project.repository.tag_names.include?(tag_name(ref))
# Prevent any changes to existing git tag unless user has permissions
:admin_project
else
:push_code
end
- user.can?(action, project)
+ if user.can?(action, project)
+ build_status_object(true)
+ else
+ build_status_object(false, "You don't have permission")
+ end
end
def forced_push?(project, oldrev, newrev)
- return false if project.empty_repo?
-
- if oldrev != Gitlab::Git::BLANK_SHA && newrev != Gitlab::Git::BLANK_SHA
- missed_refs = IO.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})).read
- missed_refs.split("\n").size > 0
- else
- false
- end
+ Gitlab::ForcePushCheck.force_push?(project, oldrev, newrev)
end
private
@@ -116,5 +129,11 @@ module Gitlab
nil
end
end
+
+ protected
+
+ def build_status_object(status, message = '')
+ GitAccessStatus.new(status, message)
+ end
end
end
diff --git a/lib/gitlab/git_access_status.rb b/lib/gitlab/git_access_status.rb
new file mode 100644
index 00000000000..3d451ecebee
--- /dev/null
+++ b/lib/gitlab/git_access_status.rb
@@ -0,0 +1,15 @@
+module Gitlab
+ class GitAccessStatus
+ attr_accessor :status, :message
+ alias_method :allowed?, :status
+
+ def initialize(status, message = '')
+ @status = status
+ @message = message
+ end
+
+ def to_json
+ {status: @status, message: @message}.to_json
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
index 9f0eb3be20f..a2177c8d548 100644
--- a/lib/gitlab/git_access_wiki.rb
+++ b/lib/gitlab/git_access_wiki.rb
@@ -1,7 +1,11 @@
module Gitlab
class GitAccessWiki < GitAccess
- def change_allowed?(user, project, change)
- user.can?(:write_wiki, project)
+ def change_access_check(user, project, change)
+ if user.can?(:write_wiki, project)
+ build_status_object(true)
+ else
+ build_status_object(false, "You don't have access")
+ end
end
end
end
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
index eb2c4e48ff2..0c85acf7e69 100644
--- a/lib/gitlab/ldap/access.rb
+++ b/lib/gitlab/ldap/access.rb
@@ -8,7 +8,7 @@ module Gitlab
attr_reader :adapter, :provider, :user
def self.open(user, &block)
- Gitlab::LDAP::Adapter.open(user.provider) do |adapter|
+ Gitlab::LDAP::Adapter.open(user.ldap_identity.provider) do |adapter|
block.call(self.new(user, adapter))
end
end
@@ -28,13 +28,13 @@ module Gitlab
def initialize(user, adapter=nil)
@adapter = adapter
@user = user
- @provider = user.provider
+ @provider = user.ldap_identity.provider
end
def allowed?
- if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter)
+ if Gitlab::LDAP::Person.find_by_dn(user.ldap_identity.extern_uid, adapter)
return true unless ldap_config.active_directory
- !Gitlab::LDAP::Person.disabled_via_active_directory?(user.extern_uid, adapter)
+ !Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter)
else
false
end
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 3176e9790a7..3ef494ba137 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -12,9 +12,10 @@ module Gitlab
class << self
def find_by_uid_and_provider(uid, provider)
# LDAP distinguished name is case-insensitive
- ::User.
+ identity = ::Identity.
where(provider: [provider, :ldap]).
where('lower(extern_uid) = ?', uid.downcase).last
+ identity && identity.user
end
end
@@ -34,15 +35,13 @@ module Gitlab
end
def find_by_email
- model.find_by(email: auth_hash.email)
+ ::User.find_by(email: auth_hash.email)
end
def update_user_attributes
- gl_user.attributes = {
- extern_uid: auth_hash.uid,
- provider: auth_hash.provider,
- email: auth_hash.email
- }
+ gl_user.email = auth_hash.email
+ gl_user.identities.build(provider: auth_hash.provider, extern_uid: auth_hash.uid)
+ gl_user
end
def changed?
diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb
index 47f62153a50..6861427864e 100644
--- a/lib/gitlab/oauth/user.rb
+++ b/lib/gitlab/oauth/user.rb
@@ -5,6 +5,8 @@
#
module Gitlab
module OAuth
+ class ForbiddenAction < StandardError; end
+
class User
attr_accessor :auth_hash, :gl_user
@@ -70,24 +72,24 @@ module Gitlab
end
def find_by_uid_and_provider
- model.where(provider: auth_hash.provider, extern_uid: auth_hash.uid).last
+ identity = Identity.find_by(provider: auth_hash.provider, extern_uid: auth_hash.uid)
+ identity && identity.user
end
def build_new_user
- model.new(user_attributes).tap do |user|
- user.skip_confirmation!
- end
+ user = ::User.new(user_attributes)
+ user.skip_confirmation!
+ user.identities.new(extern_uid: auth_hash.uid, provider: auth_hash.provider)
+ user
end
def user_attributes
{
- extern_uid: auth_hash.uid,
- provider: auth_hash.provider,
name: auth_hash.name,
username: auth_hash.username,
email: auth_hash.email,
password: auth_hash.password,
- password_confirmation: auth_hash.password,
+ password_confirmation: auth_hash.password
}
end
@@ -95,12 +97,8 @@ module Gitlab
Gitlab::AppLogger
end
- def model
- ::User
- end
-
- def raise_unauthorized_to_create
- raise StandardError.new("Unauthorized to create user, signup disabled for #{auth_hash.provider}")
+ def unauthorized_to_create
+ raise ForbiddenAction.new("Unauthorized to create user, signup disabled for #{auth_hash.provider}")
end
end
end
diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb
new file mode 100644
index 00000000000..0f2db50e98c
--- /dev/null
+++ b/lib/gitlab/sidekiq_middleware/memory_killer.rb
@@ -0,0 +1,53 @@
+module Gitlab
+ module SidekiqMiddleware
+ class MemoryKiller
+ # Default the RSS limit to 0, meaning the MemoryKiller is disabled
+ MAX_RSS = (ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] || 0).to_s.to_i
+ # Give Sidekiq 15 minutes of grace time after exceeding the RSS limit
+ GRACE_TIME = (ENV['SIDEKIQ_MEMORY_KILLER_GRACE_TIME'] || 15 * 60).to_s.to_i
+ # Wait 30 seconds for running jobs to finish during graceful shutdown
+ SHUTDOWN_WAIT = (ENV['SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT'] || 30).to_s.to_i
+
+ # Create a mutex used to ensure there will be only one thread waiting to
+ # shut Sidekiq down
+ MUTEX = Mutex.new
+
+ def call(worker, job, queue)
+ yield
+ current_rss = get_rss
+
+ return unless MAX_RSS > 0 && current_rss > MAX_RSS
+
+ Thread.new do
+ # Return if another thread is already waiting to shut Sidekiq down
+ return unless MUTEX.try_lock
+
+ Sidekiq.logger.warn "current RSS #{current_rss} exceeds maximum RSS "\
+ "#{MAX_RSS}"
+ Sidekiq.logger.warn "spawned thread that will shut down PID "\
+ "#{Process.pid} in #{GRACE_TIME} seconds"
+ sleep(GRACE_TIME)
+
+ Sidekiq.logger.warn "sending SIGUSR1 to PID #{Process.pid}"
+ Process.kill('SIGUSR1', Process.pid)
+
+ Sidekiq.logger.warn "waiting #{SHUTDOWN_WAIT} seconds before sending "\
+ "SIGTERM to PID #{Process.pid}"
+ sleep(SHUTDOWN_WAIT)
+
+ Sidekiq.logger.warn "sending SIGTERM to PID #{Process.pid}"
+ Process.kill('SIGTERM', Process.pid)
+ end
+ end
+
+ private
+
+ def get_rss
+ output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{Process.pid}))
+ return 0 unless status.zero?
+
+ output.to_i
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 2eff1260b61..0230fbb010b 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -6,6 +6,7 @@ namespace :gitlab do
desc "GITLAB | Create a backup of the GitLab system"
task create: :environment do
warn_user_is_not_gitlab
+ configure_cron_mode
Rake::Task["gitlab:backup:db:create"].invoke
Rake::Task["gitlab:backup:repo:create"].invoke
@@ -21,6 +22,7 @@ namespace :gitlab do
desc "GITLAB | Restore a previously created backup"
task restore: :environment do
warn_user_is_not_gitlab
+ configure_cron_mode
backup = Backup::Manager.new
backup.unpack
@@ -35,43 +37,54 @@ namespace :gitlab do
namespace :repo do
task create: :environment do
- puts "Dumping repositories ...".blue
+ $progress.puts "Dumping repositories ...".blue
Backup::Repository.new.dump
- puts "done".green
+ $progress.puts "done".green
end
task restore: :environment do
- puts "Restoring repositories ...".blue
+ $progress.puts "Restoring repositories ...".blue
Backup::Repository.new.restore
- puts "done".green
+ $progress.puts "done".green
end
end
namespace :db do
task create: :environment do
- puts "Dumping database ... ".blue
+ $progress.puts "Dumping database ... ".blue
Backup::Database.new.dump
- puts "done".green
+ $progress.puts "done".green
end
task restore: :environment do
- puts "Restoring database ... ".blue
+ $progress.puts "Restoring database ... ".blue
Backup::Database.new.restore
- puts "done".green
+ $progress.puts "done".green
end
end
namespace :uploads do
task create: :environment do
- puts "Dumping uploads ... ".blue
+ $progress.puts "Dumping uploads ... ".blue
Backup::Uploads.new.dump
- puts "done".green
+ $progress.puts "done".green
end
task restore: :environment do
- puts "Restoring uploads ... ".blue
+ $progress.puts "Restoring uploads ... ".blue
Backup::Uploads.new.restore
- puts "done".green
+ $progress.puts "done".green
+ end
+ end
+
+ def configure_cron_mode
+ if ENV['CRON']
+ # We need an object we can say 'puts' and 'print' to; let's use a
+ # StringIO.
+ require 'stringio'
+ $progress = StringIO.new
+ else
+ $progress = $stdout
end
end
end # namespace end: backup
diff --git a/lib/tasks/gitlab/mail_google_schema_whitelisting.rake b/lib/tasks/gitlab/mail_google_schema_whitelisting.rake
new file mode 100644
index 00000000000..f40bba24da8
--- /dev/null
+++ b/lib/tasks/gitlab/mail_google_schema_whitelisting.rake
@@ -0,0 +1,73 @@
+require "#{Rails.root}/app/helpers/emails_helper"
+require 'action_view/helpers'
+extend ActionView::Helpers
+
+include ActionView::Context
+include EmailsHelper
+
+namespace :gitlab do
+ desc "Email google whitelisting email with example email for actions in inbox"
+ task mail_google_schema_whitelisting: :environment do
+ subject = "Rails | Implemented feature"
+ url = "#{Gitlab.config.gitlab.url}/base/rails-project/issues/#{rand(1..100)}#note_#{rand(10..1000)}"
+ schema = email_action(url)
+ body = email_template(schema, url)
+ mail = Notify.test_email("schema.whitelisting+sample@gmail.com", subject, body.html_safe)
+ if send_now
+ mail.deliver
+ else
+ puts "WOULD SEND:"
+ end
+ puts mail
+ end
+
+ def email_template(schema, url)
+ "<html lang='en'>
+ <head>
+ <meta content='text/html; charset=utf-8' http-equiv='Content-Type'>
+ <title>
+ GitLab
+ </title>
+ </meta>
+ </head>
+ <style>
+ img {
+ max-width: 100%;
+ height: auto;
+ }
+ p.details {
+ font-style:italic;
+ color:#777
+ }
+ .footer p {
+ font-size:small;
+ color:#777
+ }
+ </style>
+ <body>
+ <div class='content'>
+ <div>
+ <p>I like it :+1: </p>
+ </div>
+ </div>
+
+ <div class='footer' style='margin-top: 10px;'>
+ <p>
+ <br>
+ You're receiving this notification because you are a member of the Base / Rails Project project team.
+ <a href=\"#{url}\">View it on GitLab</a>
+ #{schema}
+ </p>
+ </div>
+ </body>
+ </html>"
+ end
+
+ def send_now
+ if ENV['SEND'] == "true"
+ true
+ else
+ false
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 202e55c89ad..9af93300e08 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -17,15 +17,19 @@ namespace :gitlab do
# Clone if needed
unless File.directory?(target_dir)
- sh(*%W(git clone #{args.repo} #{target_dir}))
+ system(*%W(git clone -- #{args.repo} #{target_dir}))
end
# Make sure we're on the right tag
Dir.chdir(target_dir) do
# First try to checkout without fetching
# to avoid stalling tests if the Internet is down.
- reset = "git reset --hard $(git describe #{args.tag} || git describe origin/#{args.tag})"
- sh "#{reset} || git fetch origin && #{reset}"
+ reseted = reset_to_commit(args)
+
+ unless reseted
+ system(*%W(git fetch origin))
+ reset_to_commit(args)
+ end
config = {
user: user,
@@ -54,7 +58,7 @@ namespace :gitlab do
File.open("config.yml", "w+") {|f| f.puts config.to_yaml}
# Launch installation process
- sh "bin/install"
+ system(*%W(bin/install))
end
# Required for debian packaging with PKGR: Setup .ssh/environment with
@@ -118,5 +122,16 @@ namespace :gitlab do
puts "Quitting...".red
exit 1
end
+
+ def reset_to_commit(args)
+ tag, status = Gitlab::Popen.popen(%W(git describe -- #{args.tag}))
+
+ unless status.zero?
+ tag, status = Gitlab::Popen.popen(%W(git describe -- origin/#{args.tag}))
+ end
+
+ tag = tag.strip
+ system(*%W(git reset --hard #{tag}))
+ end
end
diff --git a/spec/factories.rb b/spec/factories.rb
index 15899d8c3c4..50580cd1334 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -5,10 +5,14 @@ FactoryGirl.define do
Faker::Lorem.sentence
end
- sequence :name, aliases: [:file_name] do
+ sequence :name do
Faker::Name.name
end
+ sequence :file_name do
+ Faker::Internet.user_name
+ end
+
sequence(:url) { Faker::Internet.uri('http') }
factory :user, aliases: [:author, :assignee, :owner, :creator] do
@@ -24,9 +28,18 @@ FactoryGirl.define do
admin true
end
- trait :ldap do
- provider 'ldapmain'
- extern_uid 'my-ldap-id'
+ factory :omniauth_user do
+ ignore do
+ extern_uid '123456'
+ provider 'ldapmain'
+ end
+
+ after(:create) do |user, evaluator|
+ user.identities << create(:identity,
+ provider: evaluator.provider,
+ extern_uid: evaluator.extern_uid
+ )
+ end
end
factory :admin, traits: [:admin]
@@ -182,4 +195,9 @@ FactoryGirl.define do
deploy_key
project
end
+
+ factory :identity do
+ provider 'ldapmain'
+ extern_uid 'my-ldap-id'
+ end
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 23314b3b1a4..60eb73e4a95 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -27,6 +27,10 @@
#
FactoryGirl.define do
+ # Project without repository
+ #
+ # Project does not have bare repository.
+ # Use this factory if you dont need repository in tests
factory :empty_project, class: 'Project' do
sequence(:name) { |n| "project#{n}" }
path { name.downcase.gsub(/\s/, '_') }
@@ -47,6 +51,20 @@ FactoryGirl.define do
end
end
+ # Project with empty repository
+ #
+ # This is a case when you just created a project
+ # but not pushed any code there yet
+ factory :project_empty_repo, parent: :empty_project do
+ after :create do |project|
+ project.create_repository
+ end
+ end
+
+ # Project with test repository
+ #
+ # Test repository source can be found at
+ # https://gitlab.com/gitlab-org/gitlab-test
factory :project, parent: :empty_project do
path { 'gitlabhq' }
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
new file mode 100644
index 00000000000..746b6fc1ac9
--- /dev/null
+++ b/spec/features/atom/users_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe "User Feed", feature: true do
+ describe "GET /" do
+ let!(:user) { create(:user) }
+
+ context "user atom feed via private token" do
+ it "should render user atom feed" do
+ visit user_path(user, :atom, private_token: user.private_token)
+ body.should have_selector("feed title")
+ end
+ end
+
+ context 'feed content' do
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project, author: user, description: '') }
+ let(:note) { create(:note, noteable: issue, author: user, note: 'Bug confirmed', project: project) }
+
+ before do
+ project.team << [user, :master]
+ issue_event(issue, user)
+ note_event(note, user)
+ visit user_path(user, :atom, private_token: user.private_token)
+ end
+
+ it "should have issue opened event" do
+ body.should have_content("#{user.name} opened issue ##{issue.iid}")
+ end
+
+ it "should have issue comment event" do
+ body.should have_content("#{user.name} commented on issue ##{issue.iid}")
+ end
+ end
+ end
+
+ def issue_event(issue, user)
+ EventCreateService.new.open_issue(issue, user)
+ end
+
+ def note_event(note, user)
+ EventCreateService.new.leave_note(note, user)
+ end
+end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 92f3a6c0929..cac409b9139 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -19,8 +19,9 @@ describe 'Comments' do
it 'should be valid' do
should have_css(".js-main-target-form", visible: true, count: 1)
find(".js-main-target-form input[type=submit]").value.should == "Add Comment"
- within(".js-main-target-form") { should_not have_link("Cancel") }
- within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) }
+ within('.js-main-target-form') do
+ expect(page).not_to have_link('Cancel')
+ end
end
describe "with text" do
@@ -31,8 +32,10 @@ describe 'Comments' do
end
it 'should have enable submit button and preview button' do
- within(".js-main-target-form") { should_not have_css(".js-comment-button[disabled]") }
- within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: true) }
+ within('.js-main-target-form') do
+ expect(page).not_to have_css('.js-comment-button[disabled]')
+ expect(page).to have_css('.js-md-preview-button', visible: true)
+ end
end
end
end
@@ -41,15 +44,17 @@ describe 'Comments' do
before do
within(".js-main-target-form") do
fill_in "note[note]", with: "This is awsome!"
- find(".js-note-preview-button").trigger("click")
+ find('.js-md-preview-button').click
click_button "Add Comment"
end
end
it 'should be added and form reset' do
should have_content("This is awsome!")
- within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") }
- within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) }
+ within('.js-main-target-form') do
+ expect(page).to have_no_field('note[note]', with: 'This is awesome!')
+ expect(page).to have_css('.js-md-preview', visible: :hidden)
+ end
within(".js-main-target-form") { should have_css(".js-note-text", visible: true) }
end
end
@@ -172,11 +177,11 @@ describe 'Comments' do
# add two separate texts and trigger previews on both
within("tr[id='#{line_code}'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "One comment on line 7"
- find(".js-note-preview-button").trigger("click")
+ find('.js-md-preview-button').click
end
within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
fill_in "note[note]", with: "Another comment on line 10"
- find(".js-note-preview-button").trigger("click")
+ find('.js-md-preview-button').click
end
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 7489e56f423..06e247aea61 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -5,9 +5,10 @@ describe IssuesFinder do
let(:user2) { create :user }
let(:project1) { create(:project) }
let(:project2) { create(:project) }
- let(:issue1) { create(:issue, assignee: user, project: project1) }
- let(:issue2) { create(:issue, assignee: user, project: project2) }
- let(:issue3) { create(:issue, assignee: user2, project: project2) }
+ let(:milestone) { create(:milestone, project: project1) }
+ let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone) }
+ let(:issue2) { create(:issue, author: user, assignee: user, project: project2) }
+ let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2) }
before do
project1.team << [user, :master]
@@ -22,37 +23,59 @@ describe IssuesFinder do
issue3
end
- it 'should filter by all' do
- params = { scope: "all", state: 'opened' }
- issues = IssuesFinder.new.execute(user, params)
- issues.size.should == 3
- end
+ context 'scope: all' do
+ it 'should filter by all' do
+ params = { scope: "all", state: 'opened' }
+ issues = IssuesFinder.new.execute(user, params)
+ issues.size.should == 3
+ end
- it 'should filter by assignee' do
- params = { scope: "assigned-to-me", state: 'opened' }
- issues = IssuesFinder.new.execute(user, params)
- issues.size.should == 2
- end
+ it 'should filter by assignee id' do
+ params = { scope: "all", assignee_id: user.id, state: 'opened' }
+ issues = IssuesFinder.new.execute(user, params)
+ issues.size.should == 2
+ end
- it 'should filter by project' do
- params = { scope: "assigned-to-me", state: 'opened', project_id: project1.id }
- issues = IssuesFinder.new.execute(user, params)
- issues.size.should == 1
- end
+ it 'should filter by author id' do
+ params = { scope: "all", author_id: user2.id, state: 'opened' }
+ issues = IssuesFinder.new.execute(user, params)
+ issues.should == [issue3]
+ end
- it 'should be empty for unauthorized user' do
- params = { scope: "all", state: 'opened' }
- issues = IssuesFinder.new.execute(nil, params)
- issues.size.should be_zero
+ it 'should filter by milestone id' do
+ params = { scope: "all", milestone_id: milestone.id, state: 'opened' }
+ issues = IssuesFinder.new.execute(user, params)
+ issues.should == [issue1]
+ end
+
+ it 'should be empty for unauthorized user' do
+ params = { scope: "all", state: 'opened' }
+ issues = IssuesFinder.new.execute(nil, params)
+ issues.size.should be_zero
+ end
+
+ it 'should not include unauthorized issues' do
+ params = { scope: "all", state: 'opened' }
+ issues = IssuesFinder.new.execute(user2, params)
+ issues.size.should == 2
+ issues.should_not include(issue1)
+ issues.should include(issue2)
+ issues.should include(issue3)
+ end
end
- it 'should not include unauthorized issues' do
- params = { scope: "all", state: 'opened' }
- issues = IssuesFinder.new.execute(user2, params)
- issues.size.should == 2
- issues.should_not include(issue1)
- issues.should include(issue2)
- issues.should include(issue3)
+ context 'personal scope' do
+ it 'should filter by assignee' do
+ params = { scope: "assigned-to-me", state: 'opened' }
+ issues = IssuesFinder.new.execute(user, params)
+ issues.size.should == 2
+ end
+
+ it 'should filter by project' do
+ params = { scope: "assigned-to-me", state: 'opened', project_id: project1.id }
+ issues = IssuesFinder.new.execute(user, params)
+ issues.size.should == 1
+ end
end
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 2db67cfdf95..07dd33b211b 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -66,6 +66,16 @@ describe ApplicationHelper do
avatar_icon(user.email).to_s.should match("/uploads/user/avatar/#{ user.id }/gitlab_logo.png")
end
+ it "should return an url for the avatar with relative url" do
+ Gitlab.config.gitlab.stub(relative_url_root: "/gitlab")
+ Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
+
+ user = create(:user)
+ user.avatar = File.open(avatar_file_path)
+ user.save!
+ avatar_icon(user.email).to_s.should match("/gitlab//uploads/user/avatar/#{ user.id }/gitlab_logo.png")
+ end
+
it "should call gravatar_icon when no avatar is present" do
user = create(:user, email: 'test@example.com')
user.save!
diff --git a/spec/helpers/oauth_helper_spec.rb b/spec/helpers/oauth_helper_spec.rb
new file mode 100644
index 00000000000..453699136e9
--- /dev/null
+++ b/spec/helpers/oauth_helper_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+
+describe OauthHelper do
+ describe "additional_providers" do
+ it 'returns all enabled providers' do
+ allow(helper).to receive(:enabled_oauth_providers) { [:twitter, :github] }
+ helper.additional_providers.should include(*[:twitter, :github])
+ end
+
+ it 'does not return ldap provider' do
+ allow(helper).to receive(:enabled_oauth_providers) { [:twitter, :ldapmain] }
+ helper.additional_providers.should include(:twitter)
+ end
+
+ it 'returns empty array' do
+ allow(helper).to receive(:enabled_oauth_providers) { [] }
+ helper.additional_providers.should == []
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index fe0a6bbdabb..66e87e57cbc 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -5,14 +5,14 @@ describe Gitlab::GitAccess do
let(:project) { create(:project) }
let(:user) { create(:user) }
- describe 'download_allowed?' do
+ describe 'download_access_check' do
describe 'master permissions' do
before { project.team << [user, :master] }
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check(user, project) }
- it { should be_true }
+ it { subject.allowed?.should be_true }
end
end
@@ -20,9 +20,9 @@ describe Gitlab::GitAccess do
before { project.team << [user, :guest] }
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check(user, project) }
- it { should be_false }
+ it { subject.allowed?.should be_false }
end
end
@@ -33,22 +33,41 @@ describe Gitlab::GitAccess do
end
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check(user, project) }
- it { should be_false }
+ it { subject.allowed?.should be_false }
end
end
describe 'without acccess to project' do
context 'pull code' do
- subject { access.download_allowed?(user, project) }
+ subject { access.download_access_check(user, project) }
- it { should be_false }
+ it { subject.allowed?.should be_false }
+ end
+ end
+
+ describe 'deploy key permissions' do
+ let(:key) { create(:deploy_key) }
+
+ context 'pull code' do
+ context 'allowed' do
+ before { key.projects << project }
+ subject { access.download_access_check(key, project) }
+
+ it { subject.allowed?.should be_true }
+ end
+
+ context 'denied' do
+ subject { access.download_access_check(key, project) }
+
+ it { subject.allowed?.should be_false }
+ end
end
end
end
- describe 'push_allowed?' do
+ describe 'push_access_check' do
def protect_feature_branch
create(:protected_branch, name: 'feature', project: project)
end
@@ -117,9 +136,9 @@ describe Gitlab::GitAccess do
permissions_matrix[role].each do |action, allowed|
context action do
- subject { access.push_allowed?(user, project, changes[action]) }
+ subject { access.push_access_check(user, project, changes[action]) }
- it { should allowed ? be_true : be_false }
+ it { subject.allowed?.should allowed ? be_true : be_false }
end
end
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index ed5785b31e6..4ff45c0c616 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -11,9 +11,9 @@ describe Gitlab::GitAccessWiki do
project.team << [user, :developer]
end
- subject { access.push_allowed?(user, project, changes) }
+ subject { access.push_access_check(user, project, changes) }
- it { should be_true }
+ it { subject.allowed?.should be_true }
end
def changes
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index f4d5a927396..4573b8696c4 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::LDAP::Access do
let(:access) { Gitlab::LDAP::Access.new user }
- let(:user) { create(:user, :ldap) }
+ let(:user) { create(:omniauth_user) }
describe :allowed? do
subject { access.allowed? }
diff --git a/spec/lib/gitlab/ldap/authentication_spec.rb b/spec/lib/gitlab/ldap/authentication_spec.rb
index 0eb7c443b8b..11fdf108756 100644
--- a/spec/lib/gitlab/ldap/authentication_spec.rb
+++ b/spec/lib/gitlab/ldap/authentication_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::LDAP::Authentication do
let(:klass) { Gitlab::LDAP::Authentication }
- let(:user) { create(:user, :ldap, extern_uid: dn) }
+ let(:user) { create(:omniauth_user, extern_uid: dn) }
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
let(:login) { 'john' }
let(:password) { 'password' }
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 726c9764e3d..f73884e6441 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -15,18 +15,18 @@ describe Gitlab::LDAP::User do
describe :find_or_create do
it "finds the user if already existing" do
- existing_user = create(:user, extern_uid: 'my-uid', provider: 'ldapmain')
+ existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
expect{ gl_user.save }.to_not change{ User.count }
end
it "connects to existing non-ldap user if the email matches" do
- existing_user = create(:user, email: 'john@example.com')
+ existing_user = create(:omniauth_user, email: 'john@example.com', provider: "twitter")
expect{ gl_user.save }.to_not change{ User.count }
existing_user.reload
- expect(existing_user.extern_uid).to eql 'my-uid'
- expect(existing_user.provider).to eql 'ldapmain'
+ expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
+ expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
end
it "creates a new user if not found" do
diff --git a/spec/lib/gitlab/oauth/user_spec.rb b/spec/lib/gitlab/oauth/user_spec.rb
index 8a83a1b2588..88307515789 100644
--- a/spec/lib/gitlab/oauth/user_spec.rb
+++ b/spec/lib/gitlab/oauth/user_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::OAuth::User do
end
describe :persisted? do
- let!(:existing_user) { create(:user, extern_uid: 'my-uid', provider: 'my-provider') }
+ let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
it "finds an existing user based on uid and provider (facebook)" do
auth = double(info: double(name: 'John'), uid: 'my-uid', provider: 'my-provider')
@@ -39,8 +39,9 @@ describe Gitlab::OAuth::User do
oauth_user.save
expect(gl_user).to be_valid
- expect(gl_user.extern_uid).to eql uid
- expect(gl_user.provider).to eql 'twitter'
+ identity = gl_user.identities.first
+ expect(identity.extern_uid).to eql uid
+ expect(identity.provider).to eql 'twitter'
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index e06e8826e5c..a0c37587b23 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -46,7 +46,7 @@ describe Notify do
token = 'kETLwRaayvigPq_x3SNM'
- subject { Notify.new_user_email(new_user.id, new_user.password, token) }
+ subject { Notify.new_user_email(new_user.id, token) }
it_behaves_like 'an email sent from GitLab'
@@ -83,7 +83,7 @@ describe Notify do
let(:example_site_path) { root_path }
let(:new_user) { create(:user, email: 'newguy@example.com', password: "securePassword") }
- subject { Notify.new_user_email(new_user.id, new_user.password) }
+ subject { Notify.new_user_email(new_user.id) }
it_behaves_like 'an email sent from GitLab'
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 6d865cfc691..8be7f733a5b 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -62,6 +62,7 @@ describe User do
it { should have_many(:assigned_issues).dependent(:destroy) }
it { should have_many(:merge_requests).dependent(:destroy) }
it { should have_many(:assigned_merge_requests).dependent(:destroy) }
+ it { should have_many(:identities).dependent(:destroy) }
end
describe "Mass assignment" do
@@ -361,24 +362,29 @@ describe User do
end
describe :ldap_user? do
- let(:user) { build(:user, :ldap) }
-
it "is true if provider name starts with ldap" do
- user.provider = 'ldapmain'
+ user = create(:omniauth_user, provider: 'ldapmain')
expect( user.ldap_user? ).to be_true
end
it "is false for other providers" do
- user.provider = 'other-provider'
+ user = create(:omniauth_user, provider: 'other-provider')
expect( user.ldap_user? ).to be_false
end
it "is false if no extern_uid is provided" do
- user.extern_uid = nil
+ user = create(:omniauth_user, extern_uid: nil)
expect( user.ldap_user? ).to be_false
end
end
+ describe :ldap_identity do
+ it "returns ldap identity" do
+ user = create :omniauth_user
+ user.ldap_identity.provider.should_not be_empty
+ end
+ end
+
describe '#full_website_url' do
let(:user) { create(:user) }
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 677b1494041..4faa1f9b964 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -26,7 +26,7 @@ describe API::API, api: true do
end
end
- describe "GET /internal/allowed" do
+ describe "POST /internal/allowed" do
context "access granted" do
before do
project.team << [user, :developer]
@@ -37,7 +37,7 @@ describe API::API, api: true do
pull(key, project)
response.status.should == 200
- response.body.should == 'true'
+ JSON.parse(response.body)["status"].should be_true
end
end
@@ -46,7 +46,7 @@ describe API::API, api: true do
push(key, project)
response.status.should == 200
- response.body.should == 'true'
+ JSON.parse(response.body)["status"].should be_true
end
end
end
@@ -61,7 +61,7 @@ describe API::API, api: true do
pull(key, project)
response.status.should == 200
- response.body.should == 'false'
+ JSON.parse(response.body)["status"].should be_false
end
end
@@ -70,7 +70,7 @@ describe API::API, api: true do
push(key, project)
response.status.should == 200
- response.body.should == 'false'
+ JSON.parse(response.body)["status"].should be_false
end
end
end
@@ -87,7 +87,7 @@ describe API::API, api: true do
pull(key, personal_project)
response.status.should == 200
- response.body.should == 'false'
+ JSON.parse(response.body)["status"].should be_false
end
end
@@ -96,7 +96,7 @@ describe API::API, api: true do
push(key, personal_project)
response.status.should == 200
- response.body.should == 'false'
+ JSON.parse(response.body)["status"].should be_false
end
end
end
@@ -114,7 +114,7 @@ describe API::API, api: true do
pull(key, project)
response.status.should == 200
- response.body.should == 'true'
+ JSON.parse(response.body)["status"].should be_true
end
end
@@ -123,7 +123,7 @@ describe API::API, api: true do
push(key, project)
response.status.should == 200
- response.body.should == 'false'
+ JSON.parse(response.body)["status"].should be_false
end
end
end
@@ -140,7 +140,7 @@ describe API::API, api: true do
archive(key, project)
response.status.should == 200
- response.body.should == 'true'
+ JSON.parse(response.body)["status"].should be_true
end
end
@@ -149,10 +149,28 @@ describe API::API, api: true do
archive(key, project)
response.status.should == 200
- response.body.should == 'false'
+ JSON.parse(response.body)["status"].should be_false
end
end
end
+
+ context 'project does not exist' do
+ it do
+ pull(key, OpenStruct.new(path_with_namespace: 'gitlab/notexists'))
+
+ response.status.should == 200
+ JSON.parse(response.body)["status"].should be_false
+ end
+ end
+
+ context 'user does not exist' do
+ it do
+ pull(OpenStruct.new(id: 0), project)
+
+ response.status.should == 200
+ JSON.parse(response.body)["status"].should be_false
+ end
+ end
end
def pull(key, project)
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 113a39b870e..1ecc79ea7ef 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -33,7 +33,7 @@ describe API::API, api: true do
response.status.should == 200
json_response.should be_an Array
json_response.first.keys.should include 'email'
- json_response.first.keys.should include 'extern_uid'
+ json_response.first.keys.should include 'identities'
json_response.first.keys.should include 'can_create_project'
end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index ea584c9802d..e6505040317 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -53,13 +53,14 @@ shared_examples "RESTful project resources" do
end
end
-# projects POST /projects(.:format) projects#create
-# new_project GET /projects/new(.:format) projects#new
-# files_project GET /:id/files(.:format) projects#files
-# edit_project GET /:id/edit(.:format) projects#edit
-# project GET /:id(.:format) projects#show
-# PUT /:id(.:format) projects#update
-# DELETE /:id(.:format) projects#destroy
+# projects POST /projects(.:format) projects#create
+# new_project GET /projects/new(.:format) projects#new
+# files_project GET /:id/files(.:format) projects#files
+# edit_project GET /:id/edit(.:format) projects#edit
+# project GET /:id(.:format) projects#show
+# PUT /:id(.:format) projects#update
+# DELETE /:id(.:format) projects#destroy
+# markdown_preview_project GET /:id/markdown_preview(.:format) projects#markdown_preview
describe ProjectsController, "routing" do
it "to #create" do
post("/projects").should route_to('projects#create')
@@ -88,6 +89,12 @@ describe ProjectsController, "routing" do
it "to #destroy" do
delete("/gitlab/gitlabhq").should route_to('projects#destroy', id: 'gitlab/gitlabhq')
end
+
+ it 'to #markdown_preview' do
+ get('/gitlab/gitlabhq/markdown_preview').should(
+ route_to('projects#markdown_preview', id: 'gitlab/gitlabhq')
+ )
+ end
end
# pages_project_wikis GET /:project_id/wikis/pages(.:format) projects/wikis#pages
@@ -387,15 +394,10 @@ describe Projects::IssuesController, "routing" do
end
end
-# preview_project_notes POST /:project_id/notes/preview(.:format) notes#preview
# project_notes GET /:project_id/notes(.:format) notes#index
# POST /:project_id/notes(.:format) notes#create
# project_note DELETE /:project_id/notes/:id(.:format) notes#destroy
describe Projects::NotesController, "routing" do
- it "to #preview" do
- post("/gitlab/gitlabhq/notes/preview").should route_to('projects/notes#preview', project_id: 'gitlab/gitlabhq')
- end
-
it_behaves_like "RESTful project resources" do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'notes' }
@@ -415,6 +417,7 @@ describe Projects::BlobController, "routing" do
it "to #show" do
get("/gitlab/gitlabhq/blob/master/app/models/project.rb").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
get("/gitlab/gitlabhq/blob/master/app/models/compare.rb").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/compare.rb')
+ get("/gitlab/gitlabhq/blob/master/app/models/diff.js").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/diff.js')
get("/gitlab/gitlabhq/blob/master/files.scss").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
end
end
diff --git a/spec/tasks/gitlab/mail_google_schema_whitelisting.rb b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb
new file mode 100644
index 00000000000..45aaf0fc90b
--- /dev/null
+++ b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+require 'rake'
+
+describe 'gitlab:mail_google_schema_whitelisting rake task' do
+ before :all do
+ Rake.application.rake_require "tasks/gitlab/task_helpers"
+ Rake.application.rake_require "tasks/gitlab/mail_google_schema_whitelisting"
+ # empty task as env is already loaded
+ Rake::Task.define_task :environment
+ end
+
+ describe 'call' do
+ before do
+ # avoid writing task output to spec progress
+ $stdout.stub :write
+ end
+
+ let :run_rake_task do
+ Rake::Task["gitlab:mail_google_schema_whitelisting"].reenable
+ Rake.application.invoke_task "gitlab:mail_google_schema_whitelisting"
+ end
+
+ it 'should run the task without errors' do
+ expect { run_rake_task }.to_not raise_error
+ end
+ end
+end