Commit e1fe1e76 authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'origin/master' into 8-10-stable

parents 05807f36 7303de91
...@@ -284,7 +284,7 @@ Style/IfWithSemicolon: ...@@ -284,7 +284,7 @@ Style/IfWithSemicolon:
# Checks that conditional statements do not have an identical line at the # Checks that conditional statements do not have an identical line at the
# end of each branch, which can validly be moved out of the conditional. # end of each branch, which can validly be moved out of the conditional.
Style/IdenticalConditionalBranches: Style/IdenticalConditionalBranches:
Enabled: false Enabled: true
# Checks the indentation of the first line of the right-hand-side of a # Checks the indentation of the first line of the right-hand-side of a
# multi-line assignment. # multi-line assignment.
......
...@@ -18,8 +18,10 @@ v 8.10.0 (unreleased) ...@@ -18,8 +18,10 @@ v 8.10.0 (unreleased)
- Fix MR-auto-close text added to description. !4836 - Fix MR-auto-close text added to description. !4836
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection) - Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates. - Add Spring EmojiOne updates.
- Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion - Fix viewing notification settings when a project is pending deletion
- Fix pagination when sorting by columns with lots of ties (like priority) - Fix pagination when sorting by columns with lots of ties (like priority)
- The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
- Updated project header design - Updated project header design
- Exclude email check from the standard health check - Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424 - Updated layout for Projects, Groups, Users on Admin area !4424
...@@ -28,8 +30,10 @@ v 8.10.0 (unreleased) ...@@ -28,8 +30,10 @@ v 8.10.0 (unreleased)
- Wildcards for protected branches. !4665 - Wildcards for protected branches. !4665
- Allow importing from Github using Personal Access Tokens. (Eric K Idema) - Allow importing from Github using Personal Access Tokens. (Eric K Idema)
- API: Todos !3188 (Robert Schilling) - API: Todos !3188 (Robert Schilling)
- API: Expose shared groups for projects and shared projects for groups !5050 (Robert Schilling)
- Add "Enabled Git access protocols" to Application Settings - Add "Enabled Git access protocols" to Application Settings
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data - PipelinesFinder uses git cache data
- Throttle the update of `project.pushes_since_gc` to 1 minute. - Throttle the update of `project.pushes_since_gc` to 1 minute.
- Check for conflicts with existing Project's wiki path when creating a new project. - Check for conflicts with existing Project's wiki path when creating a new project.
...@@ -38,6 +42,7 @@ v 8.10.0 (unreleased) ...@@ -38,6 +42,7 @@ v 8.10.0 (unreleased)
- Bump Rinku to 2.0.0 - Bump Rinku to 2.0.0
- Remove unused front-end variable -> default_issues_tracker - Remove unused front-end variable -> default_issues_tracker
- Better caching of git calls on ProjectsController#show. - Better caching of git calls on ProjectsController#show.
- Avoid to retrieve MR closes_issues as much as possible.
- Add API endpoint for a group issues !4520 (mahcsig) - Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg) - Add Bugzilla integration !4930 (iamtjg)
- Instrument Rinku usage - Instrument Rinku usage
...@@ -46,6 +51,7 @@ v 8.10.0 (unreleased) ...@@ -46,6 +51,7 @@ v 8.10.0 (unreleased)
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w) - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
- Set import_url validation to be more strict - Set import_url validation to be more strict
- Memoize MR merged/closed events retrieval - Memoize MR merged/closed events retrieval
- Don't render discussion notes when requesting diff tab through AJAX
- Add basic system information like memory and disk usage to the admin panel - Add basic system information like memory and disk usage to the admin panel
- Don't garbage collect commits that have related DB records like comments - Don't garbage collect commits that have related DB records like comments
- More descriptive message for git hooks and file locks - More descriptive message for git hooks and file locks
...@@ -55,6 +61,8 @@ v 8.10.0 (unreleased) ...@@ -55,6 +61,8 @@ v 8.10.0 (unreleased)
- Add date when user joined the team on the member page - Add date when user joined the team on the member page
- Fix 404 redirect after validation fails importing a GitLab project - Fix 404 redirect after validation fails importing a GitLab project
- Added setting to set new users by default as external !4545 (Dravere) - Added setting to set new users by default as external !4545 (Dravere)
- Add min value for project limit field on user's form !3622 (jastkand)
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
v 8.9.5 v 8.9.5
- Add more debug info to import/export and memory killer. !5108 - Add more debug info to import/export and memory killer. !5108
...@@ -99,7 +107,7 @@ v 8.9.3 ...@@ -99,7 +107,7 @@ v 8.9.3
- Removed fade when filtering results. !4932 - Removed fade when filtering results. !4932
- Fix missing avatar on system notes. !4954 - Fix missing avatar on system notes. !4954
- Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973 - Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973
- Use update_columns to by_pass all the dirty code on active_record. !4985 - Use update_columns to bypass all the dirty code on active_record. !4985
- Fix restore Rake task warning message output !4980 - Fix restore Rake task warning message output !4980
v 8.9.2 v 8.9.2
......
source "https://rubygems.org" source 'https://rubygems.org'
gem 'rails', '4.2.6' gem 'rails', '4.2.6'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
...@@ -11,11 +11,11 @@ gem 'responders', '~> 2.0' ...@@ -11,11 +11,11 @@ gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.6.0' gem 'sprockets', '~> 3.6.0'
# Default values for AR models # Default values for AR models
gem "default_value_for", "~> 3.0.0" gem 'default_value_for', '~> 3.0.0'
# Supported DBs # Supported DBs
gem "mysql2", '~> 0.3.16', group: :mysql gem 'mysql2', '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.0' gem 'devise', '~> 4.0'
...@@ -28,7 +28,7 @@ gem 'omniauth-cas3', '~> 1.1.2' ...@@ -28,7 +28,7 @@ gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 3.0.0' gem 'omniauth-facebook', '~> 3.0.0'
gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.6.0' gem 'omniauth-saml', '~> 1.6.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
...@@ -48,16 +48,16 @@ gem 'attr_encrypted', '~> 3.0.0' ...@@ -48,16 +48,16 @@ gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1' gem 'u2f', '~> 0.2.1'
# Browser detection # Browser detection
gem "browser", '~> 2.2' gem 'browser', '~> 2.2'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 10.2' gem 'gitlab_git', '~> 10.2'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap" gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki # Git Wiki
# Required manually in config/initializers/gollum.rb to control load order # Required manually in config/initializers/gollum.rb to control load order
...@@ -65,7 +65,7 @@ gem 'gollum-lib', '~> 4.1.0', require: false ...@@ -65,7 +65,7 @@ gem 'gollum-lib', '~> 4.1.0', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection # Language detection
gem "github-linguist", "~> 4.7.0", require: "linguist" gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.13.0' gem 'grape', '~> 0.13.0'
...@@ -73,13 +73,13 @@ gem 'grape-entity', '~> 0.4.2' ...@@ -73,13 +73,13 @@ gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination # Pagination
gem "kaminari", "~> 0.17.0" gem 'kaminari', '~> 0.17.0'
# HAML # HAML
gem 'hamlit', '~> 2.5' gem 'hamlit', '~> 2.5'
# Files attachments # Files attachments
gem "carrierwave", '~> 0.10.0' gem 'carrierwave', '~> 0.10.0'
# Drag and Drop UI # Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1' gem 'dropzonejs-rails', '~> 0.7.1'
...@@ -94,13 +94,13 @@ gem 'fog-openstack', '~> 0.1' ...@@ -94,13 +94,13 @@ gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1' gem 'fog-rackspace', '~> 0.1.1'
# for aws storage # for aws storage
gem "unf", '~> 0.1.4' gem 'unf', '~> 0.1.4'
# Authorization # Authorization
gem "six", '~> 0.2.0' gem 'six', '~> 0.2.0'
# Seed data # Seed data
gem "seed-fu", '~> 2.3.5' gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
...@@ -124,29 +124,29 @@ gem 'diffy', '~> 3.0.3' ...@@ -124,29 +124,29 @@ gem 'diffy', '~> 3.0.3'
# Application server # Application server
group :unicorn do group :unicorn do
gem "unicorn", '~> 4.9.0' gem 'unicorn', '~> 4.9.0'
gem 'unicorn-worker-killer', '~> 0.4.2' gem 'unicorn-worker-killer', '~> 0.4.2'
end end
# State machine # State machine
gem "state_machines-activerecord", '~> 0.4.0' gem 'state_machines-activerecord', '~> 0.4.0'
# Run events after state machine commits # Run events after state machine commits
gem 'after_commit_queue' gem 'after_commit_queue', '~> 1.3.0'
# Issue tags # Issue tags
gem 'acts-as-taggable-on', '~> 3.4' gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs # Background jobs
gem 'sinatra', '~> 1.4.4', require: nil gem 'sinatra', '~> 1.4.4', require: false
gem 'sidekiq', '~> 4.0' gem 'sidekiq', '~> 4.0'
gem 'sidekiq-cron', '~> 0.4.0' gem 'sidekiq-cron', '~> 0.4.0'
gem 'redis-namespace' gem 'redis-namespace', '~> 1.5.2'
# HTTP requests # HTTP requests
gem "httparty", '~> 0.13.3' gem 'httparty', '~> 0.13.3'
# Colored output to console # Colored output to console
gem "rainbow", '~> 2.1.0' gem 'rainbow', '~> 2.1.0'
# GitLab settings # GitLab settings
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
...@@ -156,7 +156,7 @@ gem 'settingslogic', '~> 2.0.9' ...@@ -156,7 +156,7 @@ gem 'settingslogic', '~> 2.0.9'
gem 'version_sorter', '~> 2.0.0' gem 'version_sorter', '~> 2.0.0'
# Cache # Cache
gem "redis-rails", '~> 4.0.0' gem 'redis-rails', '~> 4.0.0'
# Redis # Redis
gem 'redis', '~> 3.2' gem 'redis', '~> 3.2'
...@@ -169,13 +169,13 @@ gem 'tinder', '~> 1.10.0' ...@@ -169,13 +169,13 @@ gem 'tinder', '~> 1.10.0'
gem 'hipchat', '~> 1.5.0' gem 'hipchat', '~> 1.5.0'
# Flowdock integration # Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 1.0.1" gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
# Gemnasium integration # Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2" gem 'gemnasium-gitlab-service', '~> 0.2'
# Slack integration # Slack integration
gem "slack-notifier", "~> 1.2.0" gem 'slack-notifier', '~> 1.2.0'
# Asana integration # Asana integration
gem 'asana', '~> 0.4.0' gem 'asana', '~> 0.4.0'
...@@ -187,20 +187,20 @@ gem 'ruby-fogbugz', '~> 0.2.1' ...@@ -187,20 +187,20 @@ gem 'ruby-fogbugz', '~> 0.2.1'
gem 'd3_rails', '~> 3.5.0' gem 'd3_rails', '~> 3.5.0'
# underscore-rails # underscore-rails
gem "underscore-rails", "~> 1.8.0" gem 'underscore-rails', '~> 1.8.0'
# Sanitize user input # Sanitize user input
gem "sanitize", '~> 2.0' gem 'sanitize', '~> 2.0'
gem 'babosa', '~> 1.0.2' gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input # Sanitizes SVG input
gem "loofah", "~> 2.0.3" gem 'loofah', '~> 2.0.3'
# Working with license # Working with license
gem 'licensee', '~> 8.0.0' gem 'licensee', '~> 8.0.0'
# Protect against bruteforcing # Protect against bruteforcing
gem "rack-attack", '~> 4.3.1' gem 'rack-attack', '~> 4.3.1'
# Ace editor # Ace editor
gem 'ace-rails-ap', '~> 4.0.2' gem 'ace-rails-ap', '~> 4.0.2'
...@@ -214,9 +214,9 @@ gem 'charlock_holmes', '~> 0.7.3' ...@@ -214,9 +214,9 @@ gem 'charlock_holmes', '~> 0.7.3'
# Parse duration # Parse duration
gem 'chronic_duration', '~> 0.10.6' gem 'chronic_duration', '~> 0.10.6'
gem "sass-rails", '~> 5.0.0' gem 'sass-rails', '~> 5.0.0'
gem "coffee-rails", '~> 4.1.0' gem 'coffee-rails', '~> 4.1.0'
gem "uglifier", '~> 2.7.2' gem 'uglifier', '~> 2.7.2'
gem 'turbolinks', '~> 2.5.0' gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.1.0' gem 'jquery-turbolinks', '~> 2.1.0'
...@@ -247,13 +247,13 @@ group :metrics do ...@@ -247,13 +247,13 @@ group :metrics do
end end
group :development do group :development do
gem "foreman" gem 'foreman', '~> 0.78.0'
gem 'brakeman', '~> 3.3.0', require: false gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0' gem 'letter_opener_web', '~> 1.3.0'
gem 'rerun', '~> 0.11.0' gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false gem 'bullet', '~> 5.0.0', require: false
gem 'rblineprof', platform: :mri, require: false gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
gem 'web-console', '~> 2.0' gem 'web-console', '~> 2.0'
# Better errors handler # Better errors handler
...@@ -261,15 +261,15 @@ group :development do ...@@ -261,15 +261,15 @@ group :development do
gem 'binding_of_caller', '~> 0.7.2' gem 'binding_of_caller', '~> 0.7.2'
# Docs generator # Docs generator
gem "sdoc", '~> 0.3.20' gem 'sdoc', '~> 0.3.20'
# thin instead webrick # thin instead webrick
gem 'thin', '~> 1.7.0' gem 'thin', '~> 1.7.0'
end end
group :development, :test do group :development, :test do
gem 'byebug', platform: :mri gem 'byebug', '~> 8.2.1', platform: :mri
gem 'pry-rails' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0' gem 'fuubar', '~> 2.0.0'
...@@ -277,7 +277,7 @@ group :development, :test do ...@@ -277,7 +277,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.4.0' gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails', '~> 4.6.0' gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.5.0' gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-retry' gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2' gem 'spinach-rerun-reporter', '~> 0.0.2'
...@@ -303,14 +303,14 @@ group :development, :test do ...@@ -303,14 +303,14 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.5.0', require: false gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false gem 'simplecov', '~> 0.11.0', require: false
gem 'flog', require: false gem 'flog', '~> 4.3.2', require: false
gem 'flay', require: false gem 'flay', '~> 2.6.1', require: false
gem 'bundler-audit', require: false gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', require: false gem 'benchmark-ips', '~> 2.3.0', require: false
gem "license_finder", require: false gem 'license_finder', '~> 2.1.0', require: false
gem 'knapsack' gem 'knapsack', '~> 1.11.0'
end end
group :test do group :test do
...@@ -318,30 +318,30 @@ group :test do ...@@ -318,30 +318,30 @@ group :test do
gem 'email_spec', '~> 1.6.0' gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0' gem 'webmock', '~> 1.21.0'
gem 'test_after_commit', '~> 0.4.2' gem 'test_after_commit', '~> 0.4.2'
gem 'sham_rack' gem 'sham_rack', '~> 1.3.6'
end end
group :production do group :production do
gem "gitlab_meta", '7.0' gem 'gitlab_meta', '7.0'
end end
gem "newrelic_rpm", '~> 3.14' gem 'newrelic_rpm', '~> 3.14'
gem 'octokit', '~> 4.3.0' gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.8" gem 'mail_room', '~> 0.8'
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
## CI ## CI
gem 'activerecord-session_store', '~> 1.0.0' gem 'activerecord-session_store', '~> 1.0.0'
gem "nested_form", '~> 0.3.2' gem 'nested_form', '~> 0.3.2'
# OAuth # OAuth
gem 'oauth2', '~> 1.2.0' gem 'oauth2', '~> 1.2.0'
# Soft deletion # Soft deletion
gem "paranoia", "~> 2.0" gem 'paranoia', '~> 2.0'
# Health check # Health check
gem 'health_check', '~> 1.5.1' gem 'health_check', '~> 1.5.1'
......
...@@ -439,7 +439,7 @@ GEM ...@@ -439,7 +439,7 @@ GEM
omniauth-gitlab (1.0.1) omniauth-gitlab (1.0.1)
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-oauth2 (~> 1.0) omniauth-oauth2 (~> 1.0)
omniauth-google-oauth2 (0.2.10) omniauth-google-oauth2 (0.4.1)
addressable (~> 2.3) addressable (~> 2.3)
jwt (~> 1.0) jwt (~> 1.0)
multi_json (~> 1.3) multi_json (~> 1.3)
...@@ -806,7 +806,7 @@ DEPENDENCIES ...@@ -806,7 +806,7 @@ DEPENDENCIES
activerecord-session_store (~> 1.0.0) activerecord-session_store (~> 1.0.0)
acts-as-taggable-on (~> 3.4) acts-as-taggable-on (~> 3.4)
addressable (~> 2.3.8) addressable (~> 2.3.8)
after_commit_queue after_commit_queue (~> 1.3.0)
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.4.0) asana (~> 0.4.0)
...@@ -815,15 +815,15 @@ DEPENDENCIES ...@@ -815,15 +815,15 @@ DEPENDENCIES
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
benchmark-ips benchmark-ips (~> 2.3.0)
better_errors (~> 1.0.1) better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0) bootstrap-sass (~> 3.3.0)
brakeman (~> 3.3.0) brakeman (~> 3.3.0)
browser (~> 2.2) browser (~> 2.2)
bullet bullet (~> 5.0.0)
bundler-audit bundler-audit (~> 0.5.0)
byebug byebug (~> 8.2.1)
capybara (~> 2.6.2) capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.10.0) carrierwave (~> 0.10.0)
...@@ -844,8 +844,8 @@ DEPENDENCIES ...@@ -844,8 +844,8 @@ DEPENDENCIES
email_spec (~> 1.6.0) email_spec (~> 1.6.0)
factory_girl_rails (~> 4.6.0) factory_girl_rails (~> 4.6.0)
ffaker (~> 2.0.0) ffaker (~> 2.0.0)
flay flay (~> 2.6.1)
flog flog (~> 4.3.2)
fog-aws (~> 0.9) fog-aws (~> 0.9)
fog-azure (~> 0.0) fog-azure (~> 0.0)
fog-core (~> 1.40) fog-core (~> 1.40)
...@@ -854,7 +854,7 @@ DEPENDENCIES ...@@ -854,7 +854,7 @@ DEPENDENCIES
fog-openstack (~> 0.1) fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1) fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.6.1) font-awesome-rails (~> 4.6.1)
foreman foreman (~> 0.78.0)
fuubar (~> 2.0.0) fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6) gemojione (~> 2.6)
...@@ -881,9 +881,9 @@ DEPENDENCIES ...@@ -881,9 +881,9 @@ DEPENDENCIES
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
jwt jwt
kaminari (~> 0.17.0) kaminari (~> 0.17.0)
knapsack knapsack (~> 1.11.0)
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
license_finder license_finder (~> 2.1.0)
licensee (~> 8.0.0) licensee (~> 8.0.0)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.8) mail_room (~> 0.8)
...@@ -905,7 +905,7 @@ DEPENDENCIES ...@@ -905,7 +905,7 @@ DEPENDENCIES
omniauth-facebook (~> 3.0.0) omniauth-facebook (~> 3.0.0)
omniauth-github (~> 1.1.1) omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0) omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.2.0) omniauth-google-oauth2 (~> 0.4.1)
omniauth-kerberos (~> 0.3.0) omniauth-kerberos (~> 0.3.0)
omniauth-saml (~> 1.6.0) omniauth-saml (~> 1.6.0)
omniauth-shibboleth (~> 1.2.0) omniauth-shibboleth (~> 1.2.0)
...@@ -916,19 +916,19 @@ DEPENDENCIES ...@@ -916,19 +916,19 @@ DEPENDENCIES
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0) poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0) premailer-rails (~> 1.9.0)
pry-rails pry-rails (~> 0.3.4)
rack-attack (~> 4.3.1) rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
rails (= 4.2.6) rails (= 4.2.6)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0) rainbow (~> 2.1.0)
rblineprof rblineprof (~> 0.3.6)
rdoc (~> 3.6) rdoc (~> 3.6)
recaptcha (~> 3.0) recaptcha (~> 3.0)
redcarpet (~> 3.3.3) redcarpet (~> 3.3.3)
redis (~> 3.2) redis (~> 3.2)
redis-namespace redis-namespace (~> 1.5.2)
redis-rails (~> 4.0.0) redis-rails (~> 4.0.0)
request_store (~> 1.3.0) request_store (~> 1.3.0)
rerun (~> 0.11.0) rerun (~> 0.11.0)
...@@ -936,7 +936,7 @@ DEPENDENCIES ...@@ -936,7 +936,7 @@ DEPENDENCIES
rouge (~> 1.11) rouge (~> 1.11)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0) rspec-rails (~> 3.5.0)
rspec-retry rspec-retry (~> 0.4.5)
rubocop (~> 0.40.0) rubocop (~> 0.40.0)
rubocop-rspec (~> 1.5.0) rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
...@@ -948,7 +948,7 @@ DEPENDENCIES ...@@ -948,7 +948,7 @@ DEPENDENCIES
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
sentry-raven (~> 1.1.0) sentry-raven (~> 1.1.0)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
sham_rack sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.0) sidekiq (~> 4.0)
sidekiq-cron (~> 0.4.0) sidekiq-cron (~> 0.4.0)
......
...@@ -54,7 +54,6 @@ ...@@ -54,7 +54,6 @@
#= require_directory ./u2f #= require_directory ./u2f
#= require_directory . #= require_directory .
#= require fuzzaldrin-plus #= require fuzzaldrin-plus
#= require cropper
#= require u2f #= require u2f
window.slugify = (text) -> window.slugify = (text) ->
......
...@@ -131,7 +131,6 @@ class Dispatcher ...@@ -131,7 +131,6 @@ class Dispatcher
when 'dashboard', 'root' when 'dashboard', 'root'
shortcut_handler = new ShortcutsDashboardNavigation() shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles' when 'profiles'
new Profile()
new NotificationsForm() new NotificationsForm()
new NotificationsDropdown() new NotificationsDropdown()
when 'projects' when 'projects'
......
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
w.gl.utils.isInGroupsPage = -> w.gl.utils.isInGroupsPage = ->
return $('body').data('page').split(':')[0] is 'groups' return gl.utils.getPagePath() is 'groups'
w.gl.utils.isInProjectPage = -> w.gl.utils.isInProjectPage = ->
return $('body').data('page').split(':')[0] is 'projects' return gl.utils.getPagePath() is 'projects'
w.gl.utils.getProjectSlug = -> w.gl.utils.getProjectSlug = ->
...@@ -40,6 +40,9 @@ ...@@ -40,6 +40,9 @@
e.stopImmediatePropagation() e.stopImmediatePropagation()
return false return false
gl.utils.getPagePath = ->
return $('body').data('page').split(':')[0]
jQuery.timefor = (time, suffix, expiredLabel) -> jQuery.timefor = (time, suffix, expiredLabel) ->
......
...@@ -78,3 +78,6 @@ $ -> ...@@ -78,3 +78,6 @@ $ ->
if comment && comment.length > 1 && $title.val() == '' if comment && comment.length > 1 && $title.val() == ''
$title.val(comment[1]).change() $title.val(comment[1]).change()
if gl.utils.getPagePath() == 'profiles'
new Profile()
...@@ -175,6 +175,12 @@ ul.content-list { ...@@ -175,6 +175,12 @@ ul.content-list {
.panel > .content-list > li { .panel > .content-list > li {
padding: $gl-padding-top $gl-padding; padding: $gl-padding-top $gl-padding;
&.commit {
@media (min-width: $screen-sm-min) {
padding-left: 46px + $gl-padding;
}
}
} }
ul.controls { ul.controls {
......
...@@ -77,10 +77,10 @@ ...@@ -77,10 +77,10 @@
&.sub-nav { &.sub-nav {
text-align: center; text-align: center;
background-color: $background-color; background-color: $dark-background-color;
.container-fluid { .container-fluid {
background-color: $background-color; background-color: $dark-background-color;
margin-bottom: 0; margin-bottom: 0;
} }
......
...@@ -12,10 +12,11 @@ $sidebar-breakpoint: 1024px; ...@@ -12,10 +12,11 @@ $sidebar-breakpoint: 1024px;
/* /*
* UI elements * UI elements
*/ */
$border-color: #e5e5e5; $border-color: #e5e5e5;
$focus-border-color: #3aabf0; $focus-border-color: #3aabf0;
$table-border-color: #f0f0f0; $table-border-color: #f0f0f0;
$background-color: #fafafa; $background-color: #fafafa;
$dark-background-color: #f7f7f7;
/* /*
* Text * Text
...@@ -153,9 +154,6 @@ $warning-message-bg: #fbf2d9; ...@@ -153,9 +154,6 @@ $warning-message-bg: #fbf2d9;
$warning-message-color: #9e8e60; $warning-message-color: #9e8e60;
$warning-message-border: #f0e2bb; $warning-message-border: #f0e2bb;
/* header */
$light-grey-header: #faf9f9;
/* tanuki logo colors */ /* tanuki logo colors */
$tanuki-red: #e24329; $tanuki-red: #e24329;
$tanuki-orange: #fc6d26; $tanuki-orange: #fc6d26;
......
...@@ -9,7 +9,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -9,7 +9,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check, :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
:ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip
] ]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds] before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds] before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check] before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
...@@ -53,9 +52,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -53,9 +52,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def show def show
@note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)).
group(:commit_id).count
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -80,6 +76,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -80,6 +76,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diffs def diffs
apply_diff_view_cookie! apply_diff_view_cookie!
@merge_request_diff = @merge_request.merge_request_diff
@commit = @merge_request.diff_head_commit @commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit @base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
...@@ -109,7 +107,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -109,7 +107,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def commits def commits
respond_to do |format| respond_to do |format|
format.html { render 'show' } format.html { render 'show' }
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_commits') } } format.json do
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
render json: { html: view_to_html_string('projects/merge_requests/show/_commits') }
end
end end
end end
...@@ -308,10 +314,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -308,10 +314,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
alias_method :issuable, :merge_request alias_method :issuable, :merge_request
alias_method :awardable, :merge_request alias_method :awardable, :merge_request
def closes_issues
@closes_issues ||= @merge_request.closes_issues
end
def authorize_update_merge_request! def authorize_update_merge_request!
return render_404 unless can?(current_user, :update_merge_request, @merge_request) return render_404 unless can?(current_user, :update_merge_request, @merge_request)
end end
...@@ -340,14 +342,33 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -340,14 +342,33 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def define_show_vars def define_show_vars
@noteable = @merge_request
@commits_count = @merge_request.commits.count
@pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses if @pipeline
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@merge_request.close
end
if request.format == :html || action_name == 'show'
define_show_html_vars
end
end
# Discussion tab data is only required on html requests
def define_show_html_vars
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request) @note = @project.notes.new(noteable: @noteable)
@discussions = @merge_request.mr_and_commit_notes. @discussions = @noteable.mr_and_commit_notes.
inc_author_project_award_emoji. inc_author_project_award_emoji.
fresh. fresh.
discussions discussions
# This is not executed lazily
@notes = Banzai::NoteRenderer.render( @notes = Banzai::NoteRenderer.render(
@discussions.flatten, @discussions.flatten,
@project, @project,
...@@ -356,28 +377,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -356,28 +377,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@project_wiki, @project_wiki,
@ref @ref
) )
@noteable = @merge_request
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@merge_request_diff = @merge_request.merge_request_diff
@pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses if @pipeline
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@merge_request.close
end
end end
def define_widget_vars def define_widget_vars
@pipeline = @merge_request.pipeline @pipeline = @merge_request.pipeline
@pipelines = [@pipeline].compact @pipelines = [@pipeline].compact
closes_issues
end end
def invalid_mr def invalid_mr
......
...@@ -53,11 +53,11 @@ class ProjectsController < Projects::ApplicationController ...@@ -53,11 +53,11 @@ class ProjectsController < Projects::ApplicationController
notice: "Project '#{@project.name}' was successfully updated." notice: "Project '#{@project.name}' was successfully updated."
) )
end end
format.js
else else
format.html { render 'edit' } format.html { render 'edit' }
format.js
end end
format.js
end end
end end
......
...@@ -55,6 +55,10 @@ module MergeRequestsHelper ...@@ -55,6 +55,10 @@ module MergeRequestsHelper
end.sort.to_sentence end.sort.to_sentence
end end
def mr_closes_issues
@mr_closes_issues ||= @merge_request.closes_issues
end
def mr_change_branches_path(merge_request) def mr_change_branches_path(merge_request)
new_namespace_project_merge_request_path( new_namespace_project_merge_request_path(
@project.namespace, @project, @project.namespace, @project,
......
...@@ -23,7 +23,7 @@ class CommitRange ...@@ -23,7 +23,7 @@ class CommitRange
attr_reader :commit_from, :notation, :commit_to attr_reader :commit_from, :notation, :commit_to
attr_reader :ref_from, :ref_to attr_reader :ref_from, :ref_to
# Optional Project model # The Project model
attr_accessor :project attr_accessor :project
# The beginning and ending refs can be named or SHAs, and # The beginning and ending refs can be named or SHAs, and
...@@ -56,7 +56,7 @@ class CommitRange ...@@ -56,7 +56,7 @@ class CommitRange
# Initialize a CommitRange # Initialize a CommitRange
# #
# range_string - The String commit range. # range_string - The String commit range.
# project - An optional Project model. # project - The Project model.
# #
# Raises ArgumentError if `range_string` does not match `PATTERN`. # Raises ArgumentError if `range_string` does not match `PATTERN`.
def initialize(range_string, project) def initialize(range_string, project)
......
...@@ -112,15 +112,7 @@ class IrkerService < Service ...@@ -112,15 +112,7 @@ class IrkerService < Service
# Authorize both irc://domain.com/#chan and irc://domain.com/chan # Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil? if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
# Do not authorize irc://domain.com/ uri.to_s
if uri.fragment.nil? && uri.path.length > 1
uri.to_s
else
# Authorize irc://domain.com/smthg#chan
# The irker daemon will deal with it by concatenating smthg and
# chan, thus sending messages on #smthgchan
uri.to_s
end
end end
end end
end end
...@@ -61,19 +61,14 @@ module MergeRequests ...@@ -61,19 +61,14 @@ module MergeRequests
merge_requests.each do |merge_request| merge_requests.each do |merge_request|
if merge_request.source_branch == @branch_name || force_push? if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_diff merge_request.reload_diff
merge_request.mark_as_unchecked
else else
mr_commit_ids = merge_request.commits.map(&:id) mr_commit_ids = merge_request.commits.map(&:id)
push_commit_ids = @commits.map(&:id) push_commit_ids = @commits.map(&:id)
matches = mr_commit_ids & push_commit_ids matches = mr_commit_ids & push_commit_ids
merge_request.reload_diff if matches.any?
if matches.any?
merge_request.reload_diff
merge_request.mark_as_unchecked
else
merge_request.mark_as_unchecked
end
end end
merge_request.mark_as_unchecked
end end
end end
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
%legend Access %legend Access
.form-group .form-group
= f.label :projects_limit, class: 'control-label' = f.label :projects_limit, class: 'control-label'
.col-sm-10= f.number_field :projects_limit, class: 'form-control' .col-sm-10= f.number_field :projects_limit, min: 0, class: 'form-control'
.form-group .form-group
= f.label :can_create_group, class: 'control-label' = f.label :can_create_group, class: 'control-label'
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
- if current_user - if current_user
.pull-right .pull-right
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
= icon('plus')
New Snippet New Snippet
.oneline .oneline
......
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/cropper.js')
= page_specific_javascript_tag('profile/application.js')
- page_title "Account" - page_title "Account"
= render 'profiles/head'
- if current_user.ldap_user? - if current_user.ldap_user?
.alert.alert-info .alert.alert-info
......
- page_title "Audit Log" - page_title "Audit Log"
= render 'profiles/head'
.row.prepend-top-default .row.prepend-top-default
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
......
- page_title "Emails" - page_title "Emails"
= render 'profiles/head'
.row.prepend-top-default .row.prepend-top-default
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.form-group .form-group
= f.label :key, class: 'label-light' = f.label :key, class: 'label-light'
= f.text_area :key, class: "form-control", rows: 8, required: true = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: "Don't paste the private part of the SSH key. Paste the public part, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'."
.form-group .form-group
= f.label :title, class: 'label-light' = f.label :title, class: 'label-light'
= f.text_field :title, class: "form-control", required: true = f.text_field :title, class: "form-control", required: true
......
- page_title @key.title, "SSH Keys" - page_title @key.title, "SSH Keys"
= render 'profiles/head'
= render "key_details" = render "key_details"
- page_title "Notifications" - page_title "Notifications"
= render 'profiles/head'
%div %div
- if @user.errors.any? - if @user.errors.any?
......
- page_title "Personal Access Tokens" - page_title "Personal Access Tokens"
= render 'profiles/head'
.row.prepend-top-default .row.prepend-top-default
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
......
- page_title 'Preferences' - page_title 'Preferences'
= render 'profiles/head'
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'row prepend-top-default js-preferences-form'} do |f| = form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'row prepend-top-default js-preferences-form'} do |f|
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
......
= render 'profiles/head'
= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f| = form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f|
= form_errors(@user) = form_errors(@user)
......
- page_title 'Two-Factor Authentication', 'Account' - page_title 'Two-Factor Authentication', 'Account'
- header_title "Two-Factor Authentication", profile_two_factor_auth_path - header_title "Two-Factor Authentication", profile_two_factor_auth_path
= render 'profiles/head'
.row.prepend-top-default .row.prepend-top-default
.col-lg-3 .col-lg-3
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
= succeed '.' do = succeed '.' do
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
- if @commits.present? - if @commits_count.nonzero?
%ul.merge-request-tabs.nav-links.no-top.no-bottom %ul.merge-request-tabs.nav-links.no-top.no-bottom
%li.notes-tab %li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
%li.commits-tab %li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
Commits Commits
%span.badge= @commits.size %span.badge= @commits_count
- if @pipeline - if @pipeline
%li.builds-tab %li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do
......
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
- elsif @merge_request.can_be_merged? - elsif @merge_request.can_be_merged?
= render 'projects/merge_requests/widget/open/accept' = render 'projects/merge_requests/widget/open/accept'
- if @closes_issues.present? - if mr_closes_issues.present?
.mr-widget-footer .mr-widget-footer
%span %span
%i.fa.fa-check %i.fa.fa-check
Accepting this merge request will close #{"issue".pluralize(@closes_issues.size)} Accepting this merge request will close #{"issue".pluralize(mr_closes_issues.size)}
= succeed '.' do = succeed '.' do
!= markdown issues_sentence(@closes_issues), pipeline: :gfm, author: @merge_request.author != markdown issues_sentence(mr_closes_issues), pipeline: :gfm, author: @merge_request.author
.hidden-xs .hidden-xs
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do - if can?(current_user, :create_project_snippet, @project)
= icon('plus') = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do
New Snippet New Snippet
- if can?(current_user, :update_project_snippet, @snippet) - if can?(current_user, :update_project_snippet, @snippet)
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
Edit Edit
- if can?(current_user, :update_project_snippet, @snippet) - if can?(current_user, :update_project_snippet, @snippet)
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do
Delete Delete
.visible-xs-block.dropdown - if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } .visible-xs-block.dropdown
Options %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
%span.caret Options
.dropdown-menu.dropdown-menu-full-width %span.caret
%ul .dropdown-menu.dropdown-menu-full-width
%li %ul
= link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do - if can?(current_user, :create_project_snippet, @project)
New Snippet %li
- if can?(current_user, :update_project_snippet, @snippet) = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do
%li New Snippet
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do - if can?(current_user, :update_project_snippet, @snippet)
Edit %li
- if can?(current_user, :update_project_snippet, @snippet) = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
%li Edit
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do - if can?(current_user, :update_project_snippet, @snippet)
Delete %li
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
Delete
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
.row-content-block.top-block .row-content-block.top-block
.pull-right .pull-right
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do - if can?(current_user, :create_project_snippet, @project)
= icon('plus') = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
New Snippet New Snippet
.oneline .oneline
Share code pastes with others out of git repository Share code pastes with others out of git repository
......
.hidden-xs .hidden-xs
= link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do - if current_user
= icon('plus') = link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do
New Snippet New Snippet
- if can?(current_user, :update_personal_snippet, @snippet) - if can?(current_user, :update_personal_snippet, @snippet)
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
Edit Edit
- if can?(current_user, :admin_personal_snippet, @snippet) - if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do
Delete Delete
.visible-xs-block.dropdown - if current_user
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } .visible-xs-block.dropdown
Options %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
%span.caret Options
.dropdown-menu.dropdown-menu-full-width %span.caret
%ul .dropdown-menu.dropdown-menu-full-width
%li %ul
= link_to new_snippet_path, title: "New Snippet" do
New Snippet
- if can?(current_user, :update_personal_snippet, @snippet)
%li %li
= link_to edit_snippet_path(@snippet) do = link_to new_snippet_path, title: "New Snippet" do
Edit New Snippet
- if can?(current_user, :admin_personal_snippet, @snippet) - if can?(current_user, :update_personal_snippet, @snippet)
%li %li
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do = link_to edit_snippet_path(@snippet) do
Delete Edit
- if can?(current_user, :admin_personal_snippet, @snippet)
%li
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
Delete
...@@ -84,6 +84,7 @@ module Gitlab ...@@ -84,6 +84,7 @@ module Gitlab
config.assets.precompile << "graphs/application.js" config.assets.precompile << "graphs/application.js"
config.assets.precompile << "users/application.js" config.assets.precompile << "users/application.js"
config.assets.precompile << "network/application.js" config.assets.precompile << "network/application.js"
config.assets.precompile << "profile/application.js"
config.assets.precompile << "lib/utils/*.js" config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js" config.assets.precompile << "lib/*.js"
......
...@@ -4,12 +4,12 @@ class RemoveDuplicatedKeys < ActiveRecord::Migration ...@@ -4,12 +4,12 @@ class RemoveDuplicatedKeys < ActiveRecord::Migration
select_all("SELECT fingerprint FROM #{quote_table_name(:keys)} GROUP BY fingerprint HAVING COUNT(*) > 1").each do |row| select_all("SELECT fingerprint FROM #{quote_table_name(:keys)} GROUP BY fingerprint HAVING COUNT(*) > 1").each do |row|
fingerprint = connection.quote(row['fingerprint']) fingerprint = connection.quote(row['fingerprint'])
execute(%Q{ execute(%Q{
DELETE FROM keys DELETE FROM #{quote_table_name(:keys)}
WHERE fingerprint = #{fingerprint} WHERE fingerprint = #{fingerprint}
AND id != ( AND id != (
SELECT id FROM ( SELECT id FROM (
SELECT max(id) AS id SELECT max(id) AS id
FROM keys FROM #{quote_table_name(:keys)}
WHERE fingerprint = #{fingerprint} WHERE fingerprint = #{fingerprint}
) max_ids ) max_ids
) )
......
# Updates project records containing invalid URLs using the AddressableUrlValidator.
# This is optimized assuming the number of invalid records is low, but
# we still need to loop through all the projects with an +import_url+
# so we use batching for the latter.
#
# This migration is non-reversible as we would have to keep the old data.
class FixNoValidatableImportUrl < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
class SqlBatches
attr_reader :results, :query
def initialize(batch_size: 1000, query:)
@offset = 0
@batch_size = batch_size
@query = query
@results = []
end
def next?
@results = ActiveRecord::Base.connection.exec_query(batched_sql)
@offset += @batch_size
@results.any?
end
private
def batched_sql
"#{@query} LIMIT #{@batch_size} OFFSET #{@offset}"
end
end
# AddressableValidator - Snapshot of AddressableUrlValidator
module AddressableUrlValidatorSnap
extend self
def valid_url?(value)
return false unless value
valid_uri?(value) && valid_protocol?(value)
rescue Addressable::URI::InvalidURIError
false
end
def valid_uri?(value)
Addressable::URI.parse(value).is_a?(Addressable::URI)
end
def valid_protocol?(value)
value =~ /\A#{URI.regexp(%w(http https ssh git))}\z/
end
end
def up
unless defined?(Addressable::URI::InvalidURIError)
say('Skipping cleaning up invalid import URLs as class from Addressable is missing')
return
end
say('Nullifying empty import URLs')
nullify_empty_urls
say('Cleaning up invalid import URLs... This may take a few minutes if we have a large number of imported projects.')
process_invalid_import_urls
end
def process_invalid_import_urls
batches = SqlBatches.new(query: "SELECT id, import_url FROM projects WHERE import_url IS NOT NULL")
while batches.next?
project_ids = []
batches.results.each do |result|
project_ids << result['id'] unless valid_url?(result['import_url'])
end
process_batch(project_ids)
end
end
def process_batch(project_ids)
Thread.new do
begin
project_ids.each { |project_id| cleanup_import_url(project_id) }
ensure
ActiveRecord::Base.connection.close
end
end.join
end
def valid_url?(url)
AddressableUrlValidatorSnap.valid_url?(url)
end
def cleanup_import_url(project_id)
execute("UPDATE projects SET import_url = NULL WHERE id = #{project_id}")
end
def nullify_empty_urls
execute("UPDATE projects SET import_url = NULL WHERE import_url = ''")
end
end
...@@ -48,7 +48,8 @@ as appropriate. ...@@ -48,7 +48,8 @@ as appropriate.
This feature was [introduced][5073] in GitLab 8.10. This feature was [introduced][5073] in GitLab 8.10.
If the commit is declined or an error occurs during the Git hook check, If the commit is declined or an error occurs during the Git hook check,
the STDERR and/or SDOUT message of the hook will be present in GitLab's UI. the STDERR or STDOUT message of the hook will be present in GitLab's UI.
STDERR takes precedence over STDOUT.
![Custom message from custom Git hook](img/custom_hooks_error_msg.png) ![Custom message from custom Git hook](img/custom_hooks_error_msg.png)
......
...@@ -42,46 +42,49 @@ Parameters: ...@@ -42,46 +42,49 @@ Parameters:
```json ```json
[ [
{ {
"id": 4, "id": 9,
"description": null, "description": "foo",
"default_branch": "master", "default_branch": "master",
"tag_list": [],
"public": false, "public": false,
"visibility_level": 0, "archived": false,
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", "visibility_level": 10,
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", "ssh_url_to_repo": "git@gitlab.example.com/html5-boilerplate.git",
"web_url": "http://example.com/diaspora/diaspora-client", "http_url_to_repo": "http://gitlab.example.com/h5bp/html5-boilerplate.git",
"tag_list": [ "web_url": "http://gitlab.example.com/h5bp/html5-boilerplate",
"example", "name": "Html5 Boilerplate",
"disapora client" "name_with_namespace": "Experimental / Html5 Boilerplate",
], "path": "html5-boilerplate",
"owner": { "path_with_namespace": "h5bp/html5-boilerplate",
"id": 3,
"name": "Diaspora",
"created_at": "2013-09-30T13: 46: 02Z"
},
"name": "Diaspora Client",
"name_with_namespace": "Diaspora / Diaspora Client",
"path": "diaspora-client",
"path_with_namespace": "diaspora/diaspora-client",
"issues_enabled": true, "issues_enabled": true,
"merge_requests_enabled": true, "merge_requests_enabled": true,
"builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "builds_enabled": true,
"created_at": "2013-09-30T13: 46: 02Z", "snippets_enabled": true,
"last_activity_at": "2013-09-30T13: 46: 02Z", "created_at": "2016-04-05T21:40:50.169Z",
"creator_id": 3, "last_activity_at": "2016-04-06T16:52:08.432Z",
"shared_runners_enabled": true,
"creator_id": 1,
"namespace": { "namespace": {
"created_at": "2013-09-30T13: 46: 02Z", "id": 5,
"description": "", "name": "Experimental",
"id": 3, "path": "h5bp",
"name": "Diaspora", "owner_id": null,
"owner_id": 1, "created_at": "2016-04-05T21:40:49.152Z",
"path": "diaspora", "updated_at": "2016-04-07T08:07:48.466Z",
"updated_at": "2013-09-30T13: 46: 02Z" "description": "foo",
"avatar": {
"url": null
},
"share_with_group_lock": false,
"visibility_level": 10
}, },
"archived": false, "avatar_url": null,
"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png" "star_count": 1,
"forks_count": 0,
"open_issues_count": 3,
"public_builds": true,
"shared_with_groups": []
} }
] ]
``` ```
...@@ -96,7 +99,180 @@ GET /groups/:id ...@@ -96,7 +99,180 @@ GET /groups/:id
Parameters: Parameters:
- `id` (required) - The ID or path of a group | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or path of a group |
```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/4
```
Example response:
```json
{
"id": 4,
"name": "Twitter",
"path": "twitter",
"description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
"visibility_level": 20,
"avatar_url": null,
"web_url": "https://gitlab.example.com/groups/twitter",
"projects": [
{
"id": 7,
"description": "Voluptas veniam qui et beatae voluptas doloremque explicabo facilis.",
"default_branch": "master",
"tag_list": [],
"public": true,
"archived": false,
"visibility_level": 20,
"ssh_url_to_repo": "git@gitlab.example.com:twitter/typeahead-js.git",
"http_url_to_repo": "https://gitlab.example.com/twitter/typeahead-js.git",
"web_url": "https://gitlab.example.com/twitter/typeahead-js",
"name": "Typeahead.Js",
"name_with_namespace": "Twitter / Typeahead.Js",
"path": "typeahead-js",
"path_with_namespace": "twitter/typeahead-js",
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": true,
"created_at": "2016-06-17T07:47:25.578Z",
"last_activity_at": "2016-06-17T07:47:25.881Z",
"shared_runners_enabled": true,
"creator_id": 1,
"namespace": {
"id": 4,
"name": "Twitter",
"path": "twitter",
"owner_id": null,
"created_at": "2016-06-17T07:47:24.216Z",
"updated_at": "2016-06-17T07:47:24.216Z",
"description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
"avatar": {
"url": null
},
"share_with_group_lock": false,
"visibility_level": 20
},
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
"open_issues_count": 3,
"public_builds": true,
"shared_with_groups": []
},
{
"id": 6,
"description": "Aspernatur omnis repudiandae qui voluptatibus eaque.",
"default_branch": "master",
"tag_list": [],
"public": false,
"archived": false,
"visibility_level": 10,
"ssh_url_to_repo": "git@gitlab.example.com:twitter/flight.git",
"http_url_to_repo": "https://gitlab.example.com/twitter/flight.git",
"web_url": "https://gitlab.example.com/twitter/flight",
"name": "Flight",
"name_with_namespace": "Twitter / Flight",
"path": "flight",
"path_with_namespace": "twitter/flight",
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": true,
"created_at": "2016-06-17T07:47:24.661Z",
"last_activity_at": "2016-06-17T07:47:24.838Z",
"shared_runners_enabled": true,
"creator_id": 1,
"namespace": {
"id": 4,
"name": "Twitter",
"path": "twitter",
"owner_id": null,
"created_at": "2016-06-17T07:47:24.216Z",
"updated_at": "2016-06-17T07:47:24.216Z",
"description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
"avatar": {
"url": null
},
"share_with_group_lock": false,
"visibility_level": 20
},
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
"open_issues_count": 8,
"public_builds": true,
"shared_with_groups": []
}
],
"shared_projects": [
{
"id": 8,
"description": "Velit eveniet provident fugiat saepe eligendi autem.",
"default_branch": "master",
"tag_list": [],
"public": false,
"archived": false,
"visibility_level": 0,
"ssh_url_to_repo": "git@gitlab.example.com:h5bp/html5-boilerplate.git",
"http_url_to_repo": "https://gitlab.example.com/h5bp/html5-boilerplate.git",
"web_url": "https://gitlab.example.com/h5bp/html5-boilerplate",
"name": "Html5 Boilerplate",
"name_with_namespace": "H5bp / Html5 Boilerplate",
"path": "html5-boilerplate",
"path_with_namespace": "h5bp/html5-boilerplate",
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": true,
"created_at": "2016-06-17T07:47:27.089Z",
"last_activity_at": "2016-06-17T07:47:27.310Z",
"shared_runners_enabled": true,
"creator_id": 1,
"namespace": {
"id": 5,
"name": "H5bp",
"path": "h5bp",
"owner_id": null,
"created_at": "2016-06-17T07:47:26.621Z",
"updated_at": "2016-06-17T07:47:26.621Z",
"description": "Id consequatur rem vel qui doloremque saepe.",
"avatar": {
"url": null
},
"share_with_group_lock": false,
"visibility_level": 20
},
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
"open_issues_count": 4,
"public_builds": true,
"shared_with_groups": [
{
"group_id": 4,
"group_name": "Twitter",
"group_access_level": 30
},
{
"group_id": 3,
"group_name": "Gitlab Org",
"group_access_level": 10
}
]
}
]
}
```
## New group ## New group
...@@ -201,7 +377,8 @@ Example response: ...@@ -201,7 +377,8 @@ Example response:
"star_count": 1, "star_count": 1,
"forks_count": 0, "forks_count": 0,
"open_issues_count": 3, "open_issues_count": 3,
"public_builds": true "public_builds": true,
"shared_with_groups": []
} }
] ]
} }
......
...@@ -49,10 +49,10 @@ Parameters: ...@@ -49,10 +49,10 @@ Parameters:
"state": "active", "state": "active",
"created_at": "2012-04-29T08:46:00Z" "created_at": "2012-04-29T08:46:00Z"
}, },
"source_project_id": "2", "source_project_id": 2,
"target_project_id": "3", "target_project_id": 3,
"labels": [ ], "labels": [ ],
"description":"fixed login page css paddings", "description": "fixed login page css paddings",
"work_in_progress": false, "work_in_progress": false,
"milestone": { "milestone": {
"id": 5, "id": 5,
...@@ -113,10 +113,10 @@ Parameters: ...@@ -113,10 +113,10 @@ Parameters:
"state": "active", "state": "active",
"created_at": "2012-04-29T08:46:00Z" "created_at": "2012-04-29T08:46:00Z"
}, },
"source_project_id": "2", "source_project_id": 2,
"target_project_id": "3", "target_project_id": 3,
"labels": [ ], "labels": [ ],
"description":"fixed login page css paddings", "description": "fixed login page css paddings",
"work_in_progress": false, "work_in_progress": false,
"milestone": { "milestone": {
"id": 5, "id": 5,
...@@ -296,7 +296,7 @@ Parameters: ...@@ -296,7 +296,7 @@ Parameters:
"source_project_id": 4, "source_project_id": 4,
"target_project_id": 4, "target_project_id": 4,
"labels": [ ], "labels": [ ],
"description":"fixed login page css paddings", "description": "fixed login page css paddings",
"work_in_progress": false, "work_in_progress": false,
"milestone": { "milestone": {
"id": 5, "id": 5,
...@@ -465,7 +465,7 @@ Parameters: ...@@ -465,7 +465,7 @@ Parameters:
"source_project_id": 4, "source_project_id": 4,
"target_project_id": 4, "target_project_id": 4,
"labels": [ ], "labels": [ ],
"description":"fixed login page css paddings", "description": "fixed login page css paddings",
"work_in_progress": false, "work_in_progress": false,
"milestone": { "milestone": {
"id": 5, "id": 5,
...@@ -531,7 +531,7 @@ Parameters: ...@@ -531,7 +531,7 @@ Parameters:
"source_project_id": 4, "source_project_id": 4,
"target_project_id": 4, "target_project_id": 4,
"labels": [ ], "labels": [ ],
"description":"fixed login page css paddings", "description": "fixed login page css paddings",
"work_in_progress": false, "work_in_progress": false,
"milestone": { "milestone": {
"id": 5, "id": 5,
......
...@@ -52,7 +52,7 @@ Parameters: ...@@ -52,7 +52,7 @@ Parameters:
"owner": { "owner": {
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"created_at": "2013-09-30T13: 46: 02Z" "created_at": "2013-09-30T13:46:02Z"
}, },
"name": "Diaspora Client", "name": "Diaspora Client",
"name_with_namespace": "Diaspora / Diaspora Client", "name_with_namespace": "Diaspora / Diaspora Client",
...@@ -64,17 +64,18 @@ Parameters: ...@@ -64,17 +64,18 @@ Parameters:
"builds_enabled": true, "builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"created_at": "2013-09-30T13: 46: 02Z", "container_registry_enabled": false,
"last_activity_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"last_activity_at": "2013-09-30T13:46:02Z",
"creator_id": 3, "creator_id": 3,
"namespace": { "namespace": {
"created_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"description": "", "description": "",
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"owner_id": 1, "owner_id": 1,
"path": "diaspora", "path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z" "updated_at": "2013-09-30T13:46:02Z"
}, },
"archived": false, "archived": false,
"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
...@@ -82,7 +83,8 @@ Parameters: ...@@ -82,7 +83,8 @@ Parameters:
"forks_count": 0, "forks_count": 0,
"star_count": 0, "star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02", "runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true "public_builds": true,
"shared_with_groups": []
}, },
{ {
"id": 6, "id": 6,
...@@ -112,6 +114,7 @@ Parameters: ...@@ -112,6 +114,7 @@ Parameters:
"builds_enabled": true, "builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"container_registry_enabled": false,
"created_at": "2013-09-30T13:46:02Z", "created_at": "2013-09-30T13:46:02Z",
"last_activity_at": "2013-09-30T13:46:02Z", "last_activity_at": "2013-09-30T13:46:02Z",
"creator_id": 3, "creator_id": 3,
...@@ -140,7 +143,8 @@ Parameters: ...@@ -140,7 +143,8 @@ Parameters:
"forks_count": 0, "forks_count": 0,
"star_count": 0, "star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02", "runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true "public_builds": true,
"shared_with_groups": []
} }
] ]
``` ```
...@@ -223,7 +227,7 @@ Parameters: ...@@ -223,7 +227,7 @@ Parameters:
"owner": { "owner": {
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"created_at": "2013-09-30T13: 46: 02Z" "created_at": "2013-09-30T13:46:02Z"
}, },
"name": "Diaspora Project Site", "name": "Diaspora Project Site",
"name_with_namespace": "Diaspora / Diaspora Project Site", "name_with_namespace": "Diaspora / Diaspora Project Site",
...@@ -235,17 +239,18 @@ Parameters: ...@@ -235,17 +239,18 @@ Parameters:
"builds_enabled": true, "builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"created_at": "2013-09-30T13: 46: 02Z", "container_registry_enabled": false,
"last_activity_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"last_activity_at": "2013-09-30T13:46:02Z",
"creator_id": 3, "creator_id": 3,
"namespace": { "namespace": {
"created_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"description": "", "description": "",
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"owner_id": 1, "owner_id": 1,
"path": "diaspora", "path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z" "updated_at": "2013-09-30T13:46:02Z"
}, },
"permissions": { "permissions": {
"project_access": { "project_access": {
...@@ -262,7 +267,20 @@ Parameters: ...@@ -262,7 +267,20 @@ Parameters:
"shared_runners_enabled": true, "shared_runners_enabled": true,
"forks_count": 0, "forks_count": 0,
"star_count": 0, "star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b" "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": [
{
"group_id": 4,
"group_name": "Twitter",
"group_access_level": 30
},
{
"group_id": 3,
"group_name": "Gitlab Org",
"group_access_level": 10
}
]
} }
``` ```
...@@ -425,6 +443,7 @@ Parameters: ...@@ -425,6 +443,7 @@ Parameters:
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional) - `container_registry_enabled` (optional)
- `shared_runners_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
...@@ -449,6 +468,7 @@ Parameters: ...@@ -449,6 +468,7 @@ Parameters:
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional) - `container_registry_enabled` (optional)
- `shared_runners_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
...@@ -475,6 +495,7 @@ Parameters: ...@@ -475,6 +495,7 @@ Parameters:
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional) - `container_registry_enabled` (optional)
- `shared_runners_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `public_builds` (optional) - `public_builds` (optional)
...@@ -537,23 +558,26 @@ Example response: ...@@ -537,23 +558,26 @@ Example response:
"builds_enabled": true, "builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"created_at": "2013-09-30T13: 46: 02Z", "container_registry_enabled": false,
"last_activity_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"last_activity_at": "2013-09-30T13:46:02Z",
"creator_id": 3, "creator_id": 3,
"namespace": { "namespace": {
"created_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"description": "", "description": "",
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"owner_id": 1, "owner_id": 1,
"path": "diaspora", "path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z" "updated_at": "2013-09-30T13:46:02Z"
}, },
"archived": true, "archived": true,
"avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
"shared_runners_enabled": true, "shared_runners_enabled": true,
"forks_count": 0, "forks_count": 0,
"star_count": 1 "star_count": 1,
"public_builds": true,
"shared_with_groups": []
} }
``` ```
...@@ -600,23 +624,26 @@ Example response: ...@@ -600,23 +624,26 @@ Example response:
"builds_enabled": true, "builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"created_at": "2013-09-30T13: 46: 02Z", "container_registry_enabled": false,
"last_activity_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"last_activity_at": "2013-09-30T13:46:02Z",
"creator_id": 3, "creator_id": 3,
"namespace": { "namespace": {
"created_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"description": "", "description": "",
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"owner_id": 1, "owner_id": 1,
"path": "diaspora", "path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z" "updated_at": "2013-09-30T13:46:02Z"
}, },
"archived": true, "archived": true,
"avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
"shared_runners_enabled": true, "shared_runners_enabled": true,
"forks_count": 0, "forks_count": 0,
"star_count": 0 "star_count": 0,
"public_builds": true,
"shared_with_groups": []
} }
``` ```
...@@ -660,7 +687,7 @@ Example response: ...@@ -660,7 +687,7 @@ Example response:
"owner": { "owner": {
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"created_at": "2013-09-30T13: 46: 02Z" "created_at": "2013-09-30T13:46:02Z"
}, },
"name": "Diaspora Project Site", "name": "Diaspora Project Site",
"name_with_namespace": "Diaspora / Diaspora Project Site", "name_with_namespace": "Diaspora / Diaspora Project Site",
...@@ -672,17 +699,18 @@ Example response: ...@@ -672,17 +699,18 @@ Example response:
"builds_enabled": true, "builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"created_at": "2013-09-30T13: 46: 02Z", "container_registry_enabled": false,
"last_activity_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"last_activity_at": "2013-09-30T13:46:02Z",
"creator_id": 3, "creator_id": 3,
"namespace": { "namespace": {
"created_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"description": "", "description": "",
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"owner_id": 1, "owner_id": 1,
"path": "diaspora", "path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z" "updated_at": "2013-09-30T13:46:02Z"
}, },
"permissions": { "permissions": {
"project_access": { "project_access": {
...@@ -699,7 +727,9 @@ Example response: ...@@ -699,7 +727,9 @@ Example response:
"shared_runners_enabled": true, "shared_runners_enabled": true,
"forks_count": 0, "forks_count": 0,
"star_count": 0, "star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b" "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": []
} }
``` ```
...@@ -743,7 +773,7 @@ Example response: ...@@ -743,7 +773,7 @@ Example response:
"owner": { "owner": {
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"created_at": "2013-09-30T13: 46: 02Z" "created_at": "2013-09-30T13:46:02Z"
}, },
"name": "Diaspora Project Site", "name": "Diaspora Project Site",
"name_with_namespace": "Diaspora / Diaspora Project Site", "name_with_namespace": "Diaspora / Diaspora Project Site",
...@@ -755,17 +785,18 @@ Example response: ...@@ -755,17 +785,18 @@ Example response:
"builds_enabled": true, "builds_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"created_at": "2013-09-30T13: 46: 02Z", "container_registry_enabled": false,
"last_activity_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"last_activity_at": "2013-09-30T13:46:02Z",
"creator_id": 3, "creator_id": 3,
"namespace": { "namespace": {
"created_at": "2013-09-30T13: 46: 02Z", "created_at": "2013-09-30T13:46:02Z",
"description": "", "description": "",
"id": 3, "id": 3,
"name": "Diaspora", "name": "Diaspora",
"owner_id": 1, "owner_id": 1,
"path": "diaspora", "path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z" "updated_at": "2013-09-30T13:46:02Z"
}, },
"permissions": { "permissions": {
"project_access": { "project_access": {
...@@ -782,7 +813,9 @@ Example response: ...@@ -782,7 +813,9 @@ Example response:
"shared_runners_enabled": true, "shared_runners_enabled": true,
"forks_count": 0, "forks_count": 0,
"star_count": 0, "star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b" "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": []
} }
``` ```
...@@ -965,11 +998,11 @@ Parameters: ...@@ -965,11 +998,11 @@ Parameters:
"id": 1, "id": 1,
"url": "http://example.com/hook", "url": "http://example.com/hook",
"project_id": 3, "project_id": 3,
"push_events": "true", "push_events": true,
"issues_events": "true", "issues_events": true,
"merge_requests_events": "true", "merge_requests_events": true,
"note_events": "true", "note_events": true,
"enable_ssl_verification": "true", "enable_ssl_verification": true,
"created_at": "2012-10-12T17:04:47Z" "created_at": "2012-10-12T17:04:47Z"
} }
``` ```
...@@ -1089,8 +1122,8 @@ Parameters: ...@@ -1089,8 +1122,8 @@ Parameters:
"name": "Jeremy Ashkenas", "name": "Jeremy Ashkenas",
"email": "jashkenas@example.com" "email": "jashkenas@example.com"
}, },
"authored_date": "2013-09-07T12: 58: 21+00: 00", "authored_date": "2013-09-07T12:58:21+00:00",
"committed_date": "2013-09-07T12: 58: 21+00: 00" "committed_date": "2013-09-07T12:58:21+00:00"
}, },
"protected": false "protected": false
} }
......
...@@ -49,7 +49,7 @@ apt-get update -yqq ...@@ -49,7 +49,7 @@ apt-get update -yqq
apt-get install git -yqq apt-get install git -yqq
# Install phpunit, the tool that we will use for testing # Install phpunit, the tool that we will use for testing
curl -o /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar curl -Lo /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
chmod +x /usr/local/bin/phpunit chmod +x /usr/local/bin/phpunit
# Install mysql driver # Install mysql driver
......
...@@ -811,7 +811,7 @@ deploy: ...@@ -811,7 +811,7 @@ deploy:
It's possible to overwrite globally defined `before_script` and `after_script`: It's possible to overwrite globally defined `before_script` and `after_script`:
```yaml ```yaml
before_script before_script:
- global before script - global before script
job: job:
......
...@@ -52,5 +52,6 @@ information from database or file system ...@@ -52,5 +52,6 @@ information from database or file system
* Use red button for destructive actions (not revertable). For example removing issue. * Use red button for destructive actions (not revertable). For example removing issue.
* Use green or blue button for primary action. Primary button should be only one. * Use green or blue button for primary action. Primary button should be only one.
Do not use both green and blue button in one form. Do not use both green and blue button in one form.
* For all other cases use default white button * For all other cases use default white button.
* Text button should have only first word capitalized. So should be "Create issue" instead of "Create Issue"
...@@ -19,7 +19,7 @@ GitHub will generate an application ID and secret key for you to use. ...@@ -19,7 +19,7 @@ GitHub will generate an application ID and secret key for you to use.
- Application name: This can be anything. Consider something like "\<Organization\>'s GitLab" or "\<Your Name\>'s GitLab" or something else descriptive. - Application name: This can be anything. Consider something like "\<Organization\>'s GitLab" or "\<Your Name\>'s GitLab" or something else descriptive.
- Homepage URL: The URL to your GitLab installation. 'https://gitlab.company.com' - Homepage URL: The URL to your GitLab installation. 'https://gitlab.company.com'
- Application description: Fill this in if you wish. - Application description: Fill this in if you wish.
- Default authorization callback URL is '${YOUR_DOMAIN}/import/github/callback' - Authorization callback URL is 'http(s)://${YOUR_DOMAIN}'
1. Select "Register application". 1. Select "Register application".
1. You should now see a Client ID and Client Secret near the top right of the page (see screenshot). 1. You should now see a Client ID and Client Secret near the top right of the page (see screenshot).
......
...@@ -7,11 +7,12 @@ ...@@ -7,11 +7,12 @@
* [Newlines](#newlines) * [Newlines](#newlines)
* [Multiple underscores in words](#multiple-underscores-in-words) * [Multiple underscores in words](#multiple-underscores-in-words)
* [URL auto-linking](#url-auto-linking) * [URL auto-linking](#url-auto-linking)
* [Multiline Blockquote](#multiline-blockquote)
* [Code and Syntax Highlighting](#code-and-syntax-highlighting) * [Code and Syntax Highlighting](#code-and-syntax-highlighting)
* [Inline Diff](#inline-diff) * [Inline Diff](#inline-diff)
* [Emoji](#emoji) * [Emoji](#emoji)
* [Special GitLab references](#special-gitlab-references) * [Special GitLab references](#special-gitlab-references)
* [Task lists](#task-lists) * [Task Lists](#task-lists)
**[Standard Markdown](#standard-markdown)** **[Standard Markdown](#standard-markdown)**
...@@ -89,6 +90,37 @@ GFM will autolink almost any URL you copy and paste into your text. ...@@ -89,6 +90,37 @@ GFM will autolink almost any URL you copy and paste into your text.
* irc://irc.freenode.net/gitlab * irc://irc.freenode.net/gitlab
* http://localhost:3000 * http://localhost:3000
## Multiline Blockquote
On top of standard Markdown [blockquotes](#blockquotes), which require prepending `>` to quoted lines,
GFM supports multiline blockquotes fenced by <code>>>></code>.
```no-highlight
>>>
If you paste a message from somewhere else
that
spans
multiple lines,
you can quote that without having to manually prepend `>` to every line!
>>>
```
>>>
If you paste a message from somewhere else
that
spans
multiple lines,
you can quote that without having to manually prepend `>` to every line!
>>>
## Code and Syntax Highlighting ## Code and Syntax Highlighting
_GitLab uses the [Rouge Ruby library][rouge] for syntax highlighting. For a _GitLab uses the [Rouge Ruby library][rouge] for syntax highlighting. For a
......
...@@ -46,7 +46,7 @@ sudo -u git -H git checkout 8-10-stable-ee ...@@ -46,7 +46,7 @@ sudo -u git -H git checkout 8-10-stable-ee
```bash ```bash
cd /home/git/gitlab-shell cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v3.1.0 sudo -u git -H git checkout v3.2.0
``` ```
### 5. Update gitlab-workhorse ### 5. Update gitlab-workhorse
......
...@@ -58,6 +58,14 @@ module API ...@@ -58,6 +58,14 @@ module API
expose :path, :path_with_namespace expose :path, :path_with_namespace
end end
class SharedGroup < Grape::Entity
expose :group_id
expose :group_name do |group_link, options|
group_link.group.name
end
expose :group_access, as: :group_access_level
end
class Project < Grape::Entity class Project < Grape::Entity
expose :id, :description, :default_branch, :tag_list expose :id, :description, :default_branch, :tag_list
expose :public?, as: :public expose :public?, as: :public
...@@ -77,6 +85,9 @@ module API ...@@ -77,6 +85,9 @@ module API
expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? } expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :public_builds expose :public_builds
expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links.all, options)
end
end end
class ProjectMember < UserBasic class ProjectMember < UserBasic
...@@ -93,6 +104,7 @@ module API ...@@ -93,6 +104,7 @@ module API
class GroupDetail < Group class GroupDetail < Group
expose :projects, using: Entities::Project expose :projects, using: Entities::Project
expose :shared_projects, using: Entities::Project
end end
class GroupMember < UserBasic class GroupMember < UserBasic
......
module Banzai
module Filter
class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
REGEX = %r{
(?<code>
# Code blocks:
# ```
# Anything, including `>>>` blocks which are ignored by this filter
# ```
^```
.+?
\n```$
)
|
(?<html>
# HTML block:
# <tag>
# Anything, including `>>>` blocks which are ignored by this filter
# </tag>
^<[^>]+?>\n
.+?
\n<\/[^>]+?>$
)
|
(?:
# Blockquote:
# >>>
# Anything, including code and HTML blocks
# >>>
^>>>\n
(?<quote>
(?:
# Any character that doesn't introduce a code or HTML block
(?!
^```
|
^<[^>]+?>\n
)
.
|
# A code block
\g<code>
|
# An HTML block
\g<html>
)+?
)
\n>>>$
)
}mx.freeze
def initialize(text, context = nil, result = nil)
super text, context, result
@text = @text.delete("\r")
end
def call
@text.gsub(REGEX) do
if $~[:quote]
$~[:quote].gsub(/^/, "> ").gsub(/^> $/, ">")
else
$~[0]
end
end
end
end
end
end
...@@ -3,7 +3,8 @@ module Banzai ...@@ -3,7 +3,8 @@ module Banzai
class PreProcessPipeline < BasePipeline class PreProcessPipeline < BasePipeline
def self.filters def self.filters
FilterArray[ FilterArray[
Filter::YamlFrontMatterFilter Filter::YamlFrontMatterFilter,
Filter::BlockquoteFenceFilter,
] ]
end end
......
...@@ -133,8 +133,9 @@ module Banzai ...@@ -133,8 +133,9 @@ module Banzai
return {} if nodes.empty? return {} if nodes.empty?
ids = unique_attribute_values(nodes, attribute) ids = unique_attribute_values(nodes, attribute)
rows = collection_objects_for_ids(collection, ids)
collection.where(id: ids).each_with_object({}) do |row, hash| rows.each_with_object({}) do |row, hash|
hash[row.id] = row hash[row.id] = row
end end
end end
...@@ -153,6 +154,31 @@ module Banzai ...@@ -153,6 +154,31 @@ module Banzai
values.to_a values.to_a
end end
# Queries the collection for the objects with the given IDs.
#
# If the RequestStore module is enabled this method will only query any
# objects that have not yet been queried. For objects that have already
# been queried the object is returned from the cache.
def collection_objects_for_ids(collection, ids)
if RequestStore.active?
cache = collection_cache[collection_cache_key(collection)]
to_query = ids.map(&:to_i) - cache.keys
unless to_query.empty?
collection.where(id: to_query).each { |row| cache[row.id] = row }
end
cache.values
else
collection.where(id: ids)
end
end
# Returns the cache key to use for a collection.
def collection_cache_key(collection)
collection.respond_to?(:model) ? collection.model : collection
end
# Processes the list of HTML documents and returns an Array containing all # Processes the list of HTML documents and returns an Array containing all
# the references. # the references.
def process(documents) def process(documents)
...@@ -189,7 +215,7 @@ module Banzai ...@@ -189,7 +215,7 @@ module Banzai
end end
def find_projects_for_hash_keys(hash) def find_projects_for_hash_keys(hash)
Project.where(id: hash.keys) collection_objects_for_ids(Project, hash.keys)
end end
private private
...@@ -199,6 +225,12 @@ module Banzai ...@@ -199,6 +225,12 @@ module Banzai
def lazy(&block) def lazy(&block)
Gitlab::Lazy.new(&block) Gitlab::Lazy.new(&block)
end end
def collection_cache
RequestStore[:banzai_collection_cache] ||= Hash.new do |hash, key|
hash[key] = {}
end
end
end end
end end
end end
...@@ -73,7 +73,7 @@ module Banzai ...@@ -73,7 +73,7 @@ module Banzai
def find_users(ids) def find_users(ids)
return [] if ids.empty? return [] if ids.empty?
User.where(id: ids).to_a collection_objects_for_ids(User, ids)
end end
def find_users_for_groups(ids) def find_users_for_groups(ids)
...@@ -85,7 +85,8 @@ module Banzai ...@@ -85,7 +85,8 @@ module Banzai
def find_users_for_projects(ids) def find_users_for_projects(ids)
return [] if ids.empty? return [] if ids.empty?
Project.where(id: ids).flat_map { |p| p.team.members.to_a } collection_objects_for_ids(Project, ids).
flat_map { |p| p.team.members.to_a }
end end
end end
end end
......
...@@ -18,7 +18,7 @@ module Gitlab ...@@ -18,7 +18,7 @@ module Gitlab
end end
def commit_per_day def commit_per_day
@commit_per_day ||= (@commits.size.to_f / @duration).round(1) @commit_per_day ||= @commits.size / (@duration + 1)
end end
def collect_data def collect_data
......
...@@ -4,6 +4,8 @@ module Gitlab ...@@ -4,6 +4,8 @@ module Gitlab
regexp = URI::Parser.new.make_regexp(['http', 'https', 'ssh', 'git']) regexp = URI::Parser.new.make_regexp(['http', 'https', 'ssh', 'git'])
content.gsub(regexp) { |url| new(url).masked_url } content.gsub(regexp) { |url| new(url).masked_url }
rescue Addressable::URI::InvalidURIError
content.gsub(regexp, '')
end end
def self.valid?(url) def self.valid?(url)
......
...@@ -143,18 +143,14 @@ module Rouge ...@@ -143,18 +143,14 @@ module Rouge
'</span>' '</span>'
end end
end end
lines.join("\n") elsif @linenos == 'inline'
else lines = lines.each_with_index.map do |line, index|
if @linenos == 'inline' number = index + @linenostart
lines = lines.each_with_index.map do |line, index| "<span class=\"linenos\">#{number}</span>#{line}"
number = index + @linenostart
"<span class=\"linenos\">#{number}</span>#{line}"
end
lines.join("\n")
else
lines.join("\n")
end end
end end
lines.join("\n")
end end
def span(tok, val) def span(tok, val)
......
...@@ -2,7 +2,7 @@ FactoryGirl.define do ...@@ -2,7 +2,7 @@ FactoryGirl.define do
# Project without repository # Project without repository
# #
# Project does not have bare repository. # Project does not have bare repository.
# Use this factory if you dont need repository in tests # Use this factory if you don't need repository in tests
factory :empty_project, class: 'Project' do factory :empty_project, class: 'Project' do
sequence(:name) { |n| "project#{n}" } sequence(:name) { |n| "project#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
......
Single `>>>` inside code block:
```
# Code
>>>
# Code
```
Double `>>>` inside code block:
```txt
# Code
>>>
# Code
>>>
# Code
```
Blockquote outside code block:
> Quote
Code block inside blockquote:
> Quote
>
> ```
> # Code
> ```
>
> Quote
Single `>>>` inside code block inside blockquote:
> Quote
>
> ```
> # Code
> >>>
> # Code
> ```
>
> Quote
Double `>>>` inside code block inside blockquote:
> Quote
>
> ```
> # Code
> >>>
> # Code
> >>>
> # Code
> ```
>
> Quote
Single `>>>` inside HTML:
<pre>
# Code
>>>
# Code
</pre>
Double `>>>` inside HTML:
<pre>
# Code
>>>
# Code
>>>
# Code
</pre>
Blockquote outside HTML:
> Quote
HTML inside blockquote:
> Quote
>
> <pre>
> # Code
> </pre>
>
> Quote
Single `>>>` inside HTML inside blockquote:
> Quote
>
> <pre>
> # Code
> >>>
> # Code
> </pre>
>
> Quote
Double `>>>` inside HTML inside blockquote:
> Quote
>
> <pre>
> # Code
> >>>
> # Code
> >>>
> # Code
> </pre>
>
> Quote
Single `>>>` inside code block:
```
# Code
>>>
# Code
```
Double `>>>` inside code block:
```txt
# Code
>>>
# Code
>>>
# Code
```
Blockquote outside code block:
>>>
Quote
>>>
Code block inside blockquote:
>>>
Quote
```
# Code
```
Quote
>>>
Single `>>>` inside code block inside blockquote:
>>>
Quote
```
# Code
>>>
# Code
```
Quote
>>>
Double `>>>` inside code block inside blockquote:
>>>
Quote
```
# Code
>>>
# Code
>>>
# Code
```
Quote
>>>
Single `>>>` inside HTML:
<pre>
# Code
>>>
# Code
</pre>
Double `>>>` inside HTML:
<pre>
# Code
>>>
# Code
>>>
# Code
</pre>
Blockquote outside HTML:
>>>
Quote
>>>
HTML inside blockquote:
>>>
Quote
<pre>
# Code
</pre>
Quote
>>>
Single `>>>` inside HTML inside blockquote:
>>>
Quote
<pre>
# Code
>>>
# Code
</pre>
Quote
>>>
Double `>>>` inside HTML inside blockquote:
>>>
Quote
<pre>
# Code
>>>
# Code
>>>
# Code
</pre>
Quote
>>>
require 'rails_helper'
describe Banzai::Filter::BlockquoteFenceFilter, lib: true do
include FilterSpecHelper
it 'converts blockquote fences to blockquote lines' do
content = File.read(Rails.root.join('spec/fixtures/blockquote_fence_before.md'))
expected = File.read(Rails.root.join('spec/fixtures/blockquote_fence_after.md'))
output = filter(content)
expect(output).to eq(expected)
end
end
...@@ -234,4 +234,79 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do ...@@ -234,4 +234,79 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
to eq([project]) to eq([project])
end end
end end
describe '#collection_objects_for_ids' do
context 'with RequestStore disabled' do
it 'queries the collection directly' do
collection = User.all
expect(collection).to receive(:where).twice.and_call_original
2.times do
expect(subject.collection_objects_for_ids(collection, [user.id])).
to eq([user])
end
end
end
context 'with RequestStore enabled' do
before do
cache = Hash.new { |hash, key| hash[key] = {} }
allow(RequestStore).to receive(:active?).and_return(true)
allow(subject).to receive(:collection_cache).and_return(cache)
end
it 'queries the collection on the first call' do
expect(subject.collection_objects_for_ids(User, [user.id])).
to eq([user])
end
it 'does not query previously queried objects' do
collection = User.all
expect(collection).to receive(:where).once.and_call_original
2.times do
expect(subject.collection_objects_for_ids(collection, [user.id])).
to eq([user])
end
end
it 'casts String based IDs to Fixnums before querying objects' do
2.times do
expect(subject.collection_objects_for_ids(User, [user.id.to_s])).
to eq([user])
end
end
it 'queries any additional objects after the first call' do
other_user = create(:user)
expect(subject.collection_objects_for_ids(User, [user.id])).
to eq([user])
expect(subject.collection_objects_for_ids(User, [user.id, other_user.id])).
to eq([user, other_user])
end
it 'caches objects on a per collection class basis' do
expect(subject.collection_objects_for_ids(User, [user.id])).
to eq([user])
expect(subject.collection_objects_for_ids(Project, [project.id])).
to eq([project])
end
end
end
describe '#collection_cache_key' do
it 'returns the cache key for a Class' do
expect(subject.collection_cache_key(Project)).to eq(Project)
end
it 'returns the cache key for an ActiveRecord::Relation' do
expect(subject.collection_cache_key(Project.all)).to eq(Project)
end
end
end end
require 'spec_helper'
describe Gitlab::Graphs::Commits, lib: true do
let!(:project) { create(:project, :public, :empty_repo) }
let!(:commit1) { create(:commit, git_commit: RepoHelpers.sample_commit, project: project, committed_date: Time.now) }
let!(:commit1_yesterday) { create(:commit, git_commit: RepoHelpers.sample_commit, project: project, committed_date: 1.day.ago)}
let!(:commit2) { create(:commit, git_commit: RepoHelpers.another_sample_commit, project: project, committed_date: Time.now) }
describe '#commit_per_day' do
context 'when range is only commits from today' do
subject { described_class.new([commit2, commit1]).commit_per_day }
it { is_expected.to eq 2 }
end
end
context 'when range is only commits from today' do
subject { described_class.new([commit2, commit1]) }
describe '#commit_per_day' do
it { expect(subject.commit_per_day).to eq 2 }
end
describe '#duration' do
it { expect(subject.duration).to eq 0 }
end
end
context 'with commits from yesterday and today' do
subject { described_class.new([commit2, commit1_yesterday]) }
describe '#commit_per_day' do
it { expect(subject.commit_per_day).to eq 1 }
end
describe '#duration' do
it { expect(subject.duration).to eq 1 }
end
end
end
...@@ -45,6 +45,12 @@ describe Gitlab::UrlSanitizer, lib: true do ...@@ -45,6 +45,12 @@ describe Gitlab::UrlSanitizer, lib: true do
expect(filtered_content).to include("user@server:project.git") expect(filtered_content).to include("user@server:project.git")
end end
it 'returns an empty string for invalid URLs' do
filtered_content = sanitize_url('ssh://')
expect(filtered_content).to include("repository '' not found")
end
end end
describe '#sanitized_url' do describe '#sanitized_url' do
......
...@@ -49,10 +49,25 @@ describe API::API, api: true do ...@@ -49,10 +49,25 @@ describe API::API, api: true do
describe "GET /groups/:id" do describe "GET /groups/:id" do
context "when authenticated as user" do context "when authenticated as user" do
it "should return one of user1's groups" do it "returns one of user1's groups" do
project = create(:project, namespace: group2, path: 'Foo')
create(:project_group_link, project: project, group: group1)
get api("/groups/#{group1.id}", user1) get api("/groups/#{group1.id}", user1)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
json_response['name'] == group1.name expect(json_response['id']).to eq(group1.id)
expect(json_response['name']).to eq(group1.name)
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
expect(json_response['visibility_level']).to eq(group1.visibility_level)
expect(json_response['avatar_url']).to eq(group1.avatar_url)
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['projects']).to be_an Array
expect(json_response['projects'].length).to eq(2)
expect(json_response['shared_projects']).to be_an Array
expect(json_response['shared_projects'].length).to eq(1)
expect(json_response['shared_projects'][0]['id']).to eq(project.id)
end end
it "should not return a non existing group" do it "should not return a non existing group" do
......
...@@ -392,11 +392,47 @@ describe API::API, api: true do ...@@ -392,11 +392,47 @@ describe API::API, api: true do
before { project } before { project }
before { project_member } before { project_member }
it 'should return a project by id' do it 'returns a project by id' do
group = create(:group)
link = create(:project_group_link, project: project, group: group)
get api("/projects/#{project.id}", user) get api("/projects/#{project.id}", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['id']).to eq(project.id)
expect(json_response['description']).to eq(project.description)
expect(json_response['default_branch']).to eq(project.default_branch)
expect(json_response['tag_list']).to be_an Array
expect(json_response['public']).to be_falsey
expect(json_response['archived']).to be_falsey
expect(json_response['visibility_level']).to be_present
expect(json_response['ssh_url_to_repo']).to be_present
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
expect(json_response['owner']).to be_a Hash
expect(json_response['owner']).to be_a Hash
expect(json_response['name']).to eq(project.name) expect(json_response['name']).to eq(project.name)
expect(json_response['owner']['username']).to eq(user.username) expect(json_response['path']).to be_present
expect(json_response['issues_enabled']).to be_present
expect(json_response['merge_requests_enabled']).to be_present
expect(json_response['wiki_enabled']).to be_present
expect(json_response['builds_enabled']).to be_present
expect(json_response['snippets_enabled']).to be_present
expect(json_response['container_registry_enabled']).to be_present
expect(json_response['created_at']).to be_present
expect(json_response['last_activity_at']).to be_present
expect(json_response['shared_runners_enabled']).to be_present
expect(json_response['creator_id']).to be_present
expect(json_response['namespace']).to be_present
expect(json_response['avatar_url']).to be_nil
expect(json_response['star_count']).to be_present
expect(json_response['forks_count']).to be_present
expect(json_response['public_builds']).to be_present
expect(json_response['shared_with_groups']).to be_an Array
expect(json_response['shared_with_groups'].length).to eq(1)
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
end end
it 'should return a project by path name' do it 'should return a project by path name' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment