diff options
970 files changed, 16666 insertions, 9054 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..7e800609e6c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +CHANGELOG merge=union
\ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index a4b51008194..53ca2ca2191 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -877,7 +877,7 @@ Lint/ParenthesesAsGroupedExpression: Checks for method calls with a space before the opening parenthesis. StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' - Enabled: false + Enabled: true Lint/RequireParentheses: Description: >- diff --git a/CHANGELOG b/CHANGELOG index 286091afacf..97376c85ece 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,94 @@ -Note: The upcoming release below contains empty lines. -This helps to reduce the number of merge conflicts. -Scroll down to see the released versions of GitLab. -Please pick a random empty line to add new content. - -v 7.8.0 (unreleased) +Please view this file on the master branch, on stable branches it's out of date. + +v 7.9.0 (unreleased) + - Fix broken email images (Hannes Rosenögger) + - Fix mass SQL statements on initial push (Hannes Rosenögger) + - Add tag push notifications and normalize HipChat and Slack messages to be consistent (Stan Hu) + - Add comment notification events to HipChat and Slack services (Stan Hu) + - Add issue and merge request events to HipChat and Slack services (Stan Hu) + - Fix merge request URL passed to Webhooks. (Stan Hu) + - Fix bug that caused a server error when editing a comment to "+1" or "-1" (Stan Hu) + - Fix code preview theme setting for comments, issues, merge requests, and snippets (Stan Hu) + - Move labels/milestones tabs to sidebar + - Upgrade Rails gem to version 4.1.9. + - Improve error messages for file edit failures + - Improve UI for commits, issues and merge request lists + - Fix commit comments on first line of diff not rendering in Merge Request Discussion view. + - Improve trigger merge request hook when source project branch has been updated (Kirill Zaitsev) + - Save web edit in new branch + - Fix ordering of imported but unchanged projects (Marco Wessel) + - Mobile UI improvements: make aside content expandable + - Expose avatar_url in projects API + - Fix checkbox alignment on the application settings page. + - Generalize image upload in drag and drop in markdown to all files (Hannes Rosenögger) + - Fix mass-unassignment of issues (Robert Speicher) + - Allow user confirmation to be skipped for new users via API + - Add a service to send updates to an Irker gateway (Romain Coltel) + - Add brakeman (security scanner for Ruby on Rails) + - Slack username and channel options + - Add grouped milestones from all projects to dashboard. + - Web hook sends pusher email as well as commiter + - Add Bitbucket omniauth provider. + - Add Bitbucket importer. + - Support referencing issues to a project whose name starts with a digit + - Condense commits already in target branch when updating merge request source branch. + - Send notifications and leave system comments when bulk updating issues. + - Automatically link commit ranges to compare page: sha1...sha4 or sha1..sha4 (includes sha1 in comparison) + - Move groups page from profile to dashboard + - Starred projects page at dashboard + - Blocking user does not remove him/her from project/groups but show blocked label + - Change subject of EmailsOnPush emails to include namespace, project and branch. + - Change subject of EmailsOnPush emails to include first commit message when multiple were pushed. + - Remove confusing footer from EmailsOnPush mail body. + - Add list of changed files to EmailsOnPush emails. + - Add option to send EmailsOnPush emails from committer email if domain matches. + - Add option to disable code diffs in EmailOnPush emails. + - Wrap commit message in EmailsOnPush email. + - Send EmailsOnPush emails when deleting commits using force push. + - Fix EmailsOnPush email comparison link to include first commit. + - Fix highliht of selected lines in file + - Reject access to group/project avatar if the user doesn't have access. + - Add database migration to clean group duplicates with same path and name (Make sure you have a backup before update) + - Add GitLab active users count to rake gitlab:check + - Starred projects page at dashboard + - Make email display name configurable + - Improve json validation in hook data + - Use Emoji One + - Updated emoji help documentation to properly reference EmojiOne. + - Fix missing GitHub organisation repositories on import page. + - Added blue thmeme + - Remove annoying notice messages when create/update merge request + - Allow smb:// links in Markdown text. + - Filter merge request by title or description at Merge Requests page + - Block user if he/she was blocked in Active Directory + +v 7.8.4 + - Fix issue_tracker_id substitution in custom issue trackers + - Fix path and name duplication in namespaces + +v 7.8.3 + - Bump version of gitlab_git fixing annotated tags without message + +v 7.8.2 + - Fix service migration issue when upgrading from versions prior to 7.3 + - Fix setting of the default use project limit via admin UI + - Fix showing of already imported projects for GitLab and Gitorious importers + - Fix response of push to repository to return "Not found" if user doesn't have access + - Fix check if user is allowed to view the file attachment + - Fix import check for case sensetive namespaces + - Increase timeout for Git-over-HTTP requests to 1 hour since large pulls/pushes can take a long time. + - Properly handle autosave local storage exceptions. + - Escape wildcards when searching LDAP by username. + +v 7.8.1 + - Fix run of custom post receive hooks + - Fix migration that caused issues when upgrading to version 7.8 from versions prior to 7.3 + - Fix the warning for LDAP users about need to set password + - Fix avatars which were not shown for non logged in users + - Fix urls for the issues when relative url was enabled + +v 7.8.0 + - Fix access control and protection against XSS for note attachments and other uploads. - Replace highlight.js with rouge-fork rugments (Stefan Tatschner) - Make project search case insensitive (Hannes Rosenögger) - Include issue/mr participants in list of recipients for reassign/close/reopen emails @@ -15,67 +100,59 @@ v 7.8.0 (unreleased) - View note image attachments in new tab when clicked instead of downloading them - Improve sorting logic in UI and API. Explicitly define what sorting method is used by default - Allow more variations for commit messages closing issues (Julien Bianchi and Hannes Rosenögger) - - Fix overflow at sidebar when have several itens + - Fix overflow at sidebar when have several items - Add notes for label changes in issue and merge requests - Show tags in commit view (Hannes Rosenögger) - Only count a user's vote once on a merge request or issue (Michael Clarke) - - - - Increate font size when browse source files and diffs + - Increase font size when browse source files and diffs + - Service Templates now let you set default values for all services - Create new file in empty repository using GitLab UI - - - Ability to clone project using oauth2 token - - - Upgrade Sidekiq gem to version 3.3.0 - Stop git zombie creation during force push check - Show success/error messages for test setting button in services - Added Rubocop for code style checks - Fix commits pagination - - - Async load a branch information at the commit page - Disable blacklist validation for project names - Allow configuring protection of the default branch upon first push (Marco Wessel) - - - Add gitlab.com importer - Add an ability to login with gitlab.com - - - - Add a commit calendar to the user profile (Hannes Rosenögger) - - + - Add a commit calendar to the user profile (Hannes Rosenögger) - Submit comment on command-enter - Notify all members of a group when that group is mentioned in a comment, for example: `@gitlab-org` or `@sales`. - Extend issue clossing pattern to include "Resolve", "Resolves", "Resolved", "Resolving" and "Close" - - - Fix long broadcast message cut-off on left sidebar (Visay Keo) - Add Project Avatars (Steven Thonus and Hannes Rosenögger) - - - - - Password reset token validity increased from 2 hours to 2 days since it is also send on account creation. - - - - + - Edit group members via API - Enable raw image paste from clipboard, currently Chrome only (Marco Cyriacks) - - - - - Add action property to merge request hook (Julien Bianchi) - - - Remove duplicates from group milestone participants list. - - - - - Add a new API function that retrieves all issues assigned to a single milestone (Justin Whear and Hannes Rosenögger) - - - - - API: Access groups with their path (Julien Bianchi) - Added link to milestone and keeping resource context on smaller viewports for issues and merge requests (Jason Blanchard) - - - Allow notification email to be set separately from primary email. - - - - API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger) - - + - API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger) - Don't have Markdown preview fail for long comments/wiki pages. - - - When test web hook - show error message instead of 500 error page if connection to hook url was reset - Added support for firing system hooks on group create/destroy and adding/removing users to group (Boyan Tabakov) - Added persistent collapse button for left side nav bar (Jason Blanchard) - Prevent losing unsaved comments by automatically restoring them when comment page is loaded again. - Don't allow page to be scaled on mobile. + - Clean the username acquired from OAuth/LDAP so it doesn't fail username validation and block signing up. + - Show assignees in merge request index page (Kelvin Mutuma) + - Link head panel titles to relevant root page. + - Allow users that signed up via OAuth to set their password in order to use Git over HTTP(S). + - Show users button to share their newly created public or internal projects on twitter + - Add quick help links to the GitLab pricing and feature comparison pages. + - Fix duplicate authorized applications in user profile and incorrect application client count in admin area. + - Make sure Markdown previews always use the same styling as the eventual destination. + - Remove deprecated Group#owner_id from API + - Show projects user contributed to on user page. Show stars near project on user page. + - Improve database performance for GitLab + - Add Asana service (Jeremy Benoist) + - Improve project web hooks with extra data v 7.7.2 - Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch @@ -111,9 +188,9 @@ v 7.7.0 - When accept merge request - do merge using sidaekiq job - Enable web signups by default - Fixes for diff comments: drag-n-drop images, selecting images - - Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update + - Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update - Remove password strength indicator - + v 7.6.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3d4d8ea9b6..42b5ce22e32 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,13 @@ Please treat our volunteers with courtesy and respect, it will go a long way tow Issues and merge requests should be in English and contain appropriate language for audiences of all ages. +## Helping others + +Please help other GitLab users when you can. +The channnels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). +Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the irc channel. +You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. + ## Issue tracker To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://about.gitlab.com/subscription/) and [consulting services](http://about.gitlab.com/consultancy/) are available from [GitLab.com](http://about.gitlab.com/). @@ -56,6 +63,8 @@ Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. +To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file. + ### Merge request guidelines If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows: diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 35cee72dcbf..fe16b348d97 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.4.3 +2.5.4 @@ -30,6 +30,7 @@ gem 'omniauth-github' gem 'omniauth-shibboleth' gem 'omniauth-kerberos' gem 'omniauth-gitlab' +gem 'omniauth-bitbucket' gem 'doorkeeper', '2.1.0' gem "rack-oauth2", "~> 1.0.5" @@ -38,10 +39,10 @@ gem "browser" # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '7.0.0.rc14' +gem "gitlab_git", '7.1.0' # Ruby/Rack Git Smart-HTTP Server Handler -gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' +gem 'gitlab-grack', '~> 2.0.0.rc2', require: 'grack' # LDAP Auth gem 'gitlab_omniauth-ldap', '1.2.0', require: "omniauth-ldap" @@ -50,7 +51,7 @@ gem 'gitlab_omniauth-ldap', '1.2.0', require: "omniauth-ldap" gem 'gollum-lib', '~> 4.0.0' # Language detection -gem "gitlab-linguist", "~> 3.0.0", require: "linguist" +gem "gitlab-linguist", "~> 3.0.1", require: "linguist" # API gem "grape", "~> 0.6.1" @@ -87,7 +88,7 @@ gem "six" gem "seed-fu" # Markup pipeline for GitLab -gem 'html-pipeline-gitlab', '~> 0.1.0' +gem 'html-pipeline-gitlab', '~> 0.1' # Markdown to HTML gem "github-markup" @@ -176,8 +177,8 @@ gem 'ace-rails-ap' # Keyboard shortcuts gem 'mousetrap-rails' -# Semantic UI Sass for Sidebar -gem 'semantic-ui-sass', '~> 1.8.0' +# Shutting down requests that take too long +gem "slowpoke" gem "sass-rails", '~> 4.0.2' gem "coffee-rails" @@ -193,7 +194,7 @@ gem "jquery-scrollto-rails" gem "raphael-rails", "~> 2.1.2" gem 'bootstrap-sass', '~> 3.0' gem "font-awesome-rails", '~> 4.2' -gem "gitlab_emoji", "~> 0.0.1.1" +gem "gitlab_emoji", "~> 0.1" gem "gon", '~> 5.0.0' gem 'nprogress-rails' gem 'request_store' @@ -201,10 +202,12 @@ gem "virtus" gem 'addressable' group :development do + gem 'brakeman', require: false gem "annotate", "~> 2.6.0.beta2" gem "letter_opener" gem 'quiet_assets', '~> 1.0.1' gem 'rack-mini-profiler', require: false + gem "byebug" # Better errors handler gem 'better_errors' @@ -222,7 +225,7 @@ group :development, :test do gem 'rubocop', '0.28.0', require: false # gem 'rails-dev-tweaks' gem 'spinach-rails' - gem "rspec-rails" + gem "rspec-rails", '2.99' gem "capybara", '~> 2.2.1' gem "pry-rails" gem "awesome_print" @@ -250,8 +253,8 @@ group :development, :test do gem 'jasmine', '2.0.2' - gem "spring", '1.1.3' - gem "spring-commands-rspec", '1.0.1' + gem "spring", '~> 1.3.1' + gem "spring-commands-rspec", '1.0.4' gem "spring-commands-spinach", '1.0.0' end diff --git a/Gemfile.lock b/Gemfile.lock index aef30046d33..c847424a7c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,31 +3,31 @@ GEM specs: RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.1.1) - actionpack (= 4.1.1) - actionview (= 4.1.1) - mail (~> 2.5.4) - actionpack (4.1.1) - actionview (= 4.1.1) - activesupport (= 4.1.1) + actionmailer (4.1.9) + actionpack (= 4.1.9) + actionview (= 4.1.9) + mail (~> 2.5, >= 2.5.4) + actionpack (4.1.9) + actionview (= 4.1.9) + activesupport (= 4.1.9) rack (~> 1.5.2) rack-test (~> 0.6.2) - actionview (4.1.1) - activesupport (= 4.1.1) + actionview (4.1.9) + activesupport (= 4.1.9) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.1) - activesupport (= 4.1.1) + activemodel (4.1.9) + activesupport (= 4.1.9) builder (~> 3.1) - activerecord (4.1.1) - activemodel (= 4.1.1) - activesupport (= 4.1.1) + activerecord (4.1.9) + activemodel (= 4.1.9) + activesupport (= 4.1.9) arel (~> 5.0.0) activeresource (4.0.0) activemodel (~> 4.0) activesupport (~> 4.0) rails-observers (~> 0.1.1) - activesupport (4.1.1) + activesupport (4.1.9) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -47,6 +47,9 @@ GEM astrolabe (1.3.0) parser (>= 2.2.0.pre.3, < 3.0) attr_required (1.0.0) + autoprefixer-rails (5.1.6) + execjs + json awesome_print (1.2.0) axiom-types (0.0.5) descendants_tracker (~> 0.0.1) @@ -57,10 +60,24 @@ GEM erubis (>= 2.6.6) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bootstrap-sass (3.0.3.0) - sass (~> 3.2) + bootstrap-sass (3.3.3) + autoprefixer-rails (>= 5.0.0.1) + sass (>= 3.2.19) + brakeman (3.0.1) + erubis (~> 2.6) + fastercsv (~> 1.5) + haml (>= 3.0, < 5.0) + highline (~> 1.6.20) + multi_json (~> 1.2) + ruby2ruby (~> 2.1.1) + ruby_parser (~> 3.5.0) + sass (~> 3.0) + terminal-table (~> 1.4) browser (0.7.2) builder (3.2.2) + byebug (3.2.0) + columnize (~> 0.8) + debugger-linecache (~> 1.2) cal-heatmap-rails (0.0.1) capybara (2.2.1) mime-types (>= 1.16) @@ -88,6 +105,7 @@ GEM coffee-script-source (1.6.3) colored (1.2) colorize (0.5.8) + columnize (0.9.0) connection_pool (2.1.0) coveralls (0.7.0) multi_json (~> 1.3) @@ -103,6 +121,7 @@ GEM daemons (1.1.9) database_cleaner (1.3.0) debug_inspector (0.0.2) + debugger-linecache (1.2.0) default_value_for (3.0.0) activerecord (>= 3.2.0, < 5.0) descendants_tracker (0.0.3) @@ -125,11 +144,10 @@ GEM email_spec (1.5.0) launchy (~> 2.1) mail (~> 2.2) - emoji (1.0.1) - json enumerize (0.7.0) activesupport (>= 3.2) equalizer (0.0.8) + errbase (0.0.2) erubis (2.7.0) escape_utils (0.2.4) eventmachine (1.0.4) @@ -145,6 +163,7 @@ GEM multipart-post (~> 1.2.0) faraday_middleware (0.9.0) faraday (>= 0.7.4, < 0.9) + fastercsv (1.5.5) ffaker (1.22.1) ffi (1.9.3) fog (1.21.0) @@ -170,8 +189,10 @@ GEM dotenv (>= 0.7) thor (>= 0.13.6) formatador (0.2.4) - gemnasium-gitlab-service (0.2.3) - rugged (~> 0.19) + gemnasium-gitlab-service (0.2.5) + rugged (~> 0.21) + gemojione (2.0.0) + json gherkin-ruby (0.3.1) racc github-markup (1.3.1) @@ -179,20 +200,20 @@ GEM gitlab-flowdock-git-hook (0.4.2.2) gitlab-grit (>= 2.4.1) multi_json - gitlab-grack (2.0.0.pre) + gitlab-grack (2.0.0.rc2) rack (~> 1.5.1) gitlab-grit (2.7.2) charlock_holmes (~> 0.6) diff-lcs (~> 1.1) mime-types (~> 1.15) posix-spawn (~> 0.3) - gitlab-linguist (3.0.0) + gitlab-linguist (3.0.1) charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.4) mime-types (~> 1.19) - gitlab_emoji (0.0.1.1) - emoji (~> 1.0.1) - gitlab_git (7.0.0.rc14) + gitlab_emoji (0.1.0) + gemojione (~> 2.0) + gitlab_git (7.1.0) activesupport (~> 4.0) charlock_holmes (~> 0.6) gitlab-linguist (~> 3.0) @@ -249,6 +270,7 @@ GEM haml (>= 3.1, < 5.0) railties (>= 4.0.1) hashie (2.1.2) + highline (1.6.21) hike (1.2.3) hipchat (1.4.0) httparty @@ -256,10 +278,11 @@ GEM html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) - html-pipeline-gitlab (0.1.5) + html-pipeline-gitlab (0.2.0) actionpack (~> 4) - gitlab_emoji (~> 0.0.1) + gitlab_emoji (~> 0.1) html-pipeline (~> 1.11.0) + mime-types sanitize (~> 2.1) http_parser.rb (0.5.3) httparty (0.13.0) @@ -303,9 +326,8 @@ GEM rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) lumberjack (1.0.4) - mail (2.5.4) - mime-types (~> 1.16) - treetop (~> 1.4.8) + mail (2.6.3) + mime-types (>= 1.16, < 3) method_source (0.8.2) mime-types (1.25.1) mini_portile (0.6.1) @@ -335,6 +357,10 @@ GEM omniauth (1.1.4) hashie (>= 1.2, < 3) rack + omniauth-bitbucket (0.0.2) + multi_json (~> 1.7) + omniauth (~> 1.1) + omniauth-oauth (~> 1.0) omniauth-github (1.1.1) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) @@ -372,7 +398,6 @@ GEM cliver (~> 0.3.1) multi_json (~> 1.0) websocket-driver (>= 0.2.0) - polyglot (0.3.4) posix-spawn (0.3.9) powerpack (0.0.9) pry (0.9.12.4) @@ -403,30 +428,31 @@ GEM rack (>= 1.1) rack-protection (1.5.1) rack - rack-test (0.6.2) + rack-test (0.6.3) rack (>= 1.0) - rails (4.1.1) - actionmailer (= 4.1.1) - actionpack (= 4.1.1) - actionview (= 4.1.1) - activemodel (= 4.1.1) - activerecord (= 4.1.1) - activesupport (= 4.1.1) + rack-timeout (0.2.0) + rails (4.1.9) + actionmailer (= 4.1.9) + actionpack (= 4.1.9) + actionview (= 4.1.9) + activemodel (= 4.1.9) + activerecord (= 4.1.9) + activesupport (= 4.1.9) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.1) + railties (= 4.1.9) sprockets-rails (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) rails_autolink (1.1.6) rails (> 3.1) - railties (4.1.1) - actionpack (= 4.1.1) - activesupport (= 4.1.1) + railties (4.1.9) + actionpack (= 4.1.9) + activesupport (= 4.1.9) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) raindrops (0.13.0) - rake (10.3.2) + rake (10.4.2) raphael-rails (2.1.2) rb-fsevent (0.9.3) rb-inotify (0.9.2) @@ -458,22 +484,28 @@ GEM rest-client (1.6.7) mime-types (>= 1.16) rinku (1.7.3) + robustly (0.0.3) + errbase rouge (1.7.4) - rspec (2.14.1) - rspec-core (~> 2.14.0) - rspec-expectations (~> 2.14.0) - rspec-mocks (~> 2.14.0) - rspec-core (2.14.7) - rspec-expectations (2.14.4) + rspec (2.99.0) + rspec-core (~> 2.99.0) + rspec-expectations (~> 2.99.0) + rspec-mocks (~> 2.99.0) + rspec-collection_matchers (1.1.2) + rspec-expectations (>= 2.99.0.beta1) + rspec-core (2.99.2) + rspec-expectations (2.99.2) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.4) - rspec-rails (2.14.0) + rspec-mocks (2.99.3) + rspec-rails (2.99.0) actionpack (>= 3.0) + activemodel (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 2.14.0) - rspec-expectations (~> 2.14.0) - rspec-mocks (~> 2.14.0) + rspec-collection_matchers + rspec-core (~> 2.99.0) + rspec-expectations (~> 2.99.0) + rspec-mocks (~> 2.99.0) rubocop (0.28.0) astrolabe (~> 1.3) parser (>= 2.2.0.pre.7, < 3.0) @@ -481,10 +513,15 @@ GEM rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) ruby-progressbar (1.7.1) + ruby2ruby (2.1.3) + ruby_parser (~> 3.1) + sexp_processor (~> 4.0) + ruby_parser (3.5.0) + sexp_processor (~> 4.1) rubyntlm (0.4.0) rubypants (0.2.0) - rugged (0.21.2) - rugments (1.0.0.beta3) + rugged (0.21.4) + rugments (1.0.0.beta4) safe_yaml (0.9.7) sanitize (2.1.0) nokogiri (>= 1.4.4) @@ -505,9 +542,8 @@ GEM activesupport (>= 3.1, < 4.2) select2-rails (3.5.2) thor (~> 0.14) - semantic-ui-sass (1.8.0.0) - sass (~> 3.2) settingslogic (2.0.9) + sexp_processor (4.4.5) shoulda-matchers (2.7.0) activesupport (>= 3.0.0) sidekiq (3.3.0) @@ -532,6 +568,9 @@ GEM temple (~> 0.6.6) tilt (>= 1.3.3, < 2.1) slop (3.6.0) + slowpoke (0.0.5) + rack-timeout (>= 0.1.0) + robustly spinach (0.8.7) colorize (= 0.5.8) gherkin-ruby (>= 0.3.1) @@ -539,8 +578,8 @@ GEM capybara (>= 2.0.0) railties (>= 3) spinach (>= 0.4) - spring (1.1.3) - spring-commands-rspec (1.0.1) + spring (1.3.3) + spring-commands-rspec (1.0.4) spring (>= 0.9.1) spring-commands-spinach (1.0.0) spring (>= 0.9.1) @@ -549,16 +588,17 @@ GEM multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.1.3) + sprockets-rails (2.2.4) actionpack (>= 3.0) activesupport (>= 3.0) - sprockets (~> 2.8) + sprockets (>= 2.8, < 4.0) stamp (0.5.0) state_machine (1.2.0) stringex (2.5.2) temple (0.6.7) term-ansicolor (1.2.2) tins (~> 0.8) + terminal-table (1.4.5) test_after_commit (0.2.2) therubyracer (0.12.0) libv8 (~> 3.16.14.0) @@ -568,7 +608,7 @@ GEM eventmachine (>= 1.0.0) rack (>= 1.0.0) thor (0.19.1) - thread_safe (0.3.4) + thread_safe (0.3.5) tilt (1.4.1) timers (4.0.1) hitimes @@ -583,9 +623,6 @@ GEM multi_json (~> 1.7) twitter-stream (~> 0.1) tins (0.13.1) - treetop (1.4.15) - polyglot - polyglot (>= 0.3.1) turbolinks (2.0.0) coffee-rails twitter-stream (0.1.16) @@ -607,7 +644,7 @@ GEM raindrops (~> 0.7) unicorn-worker-killer (0.4.2) unicorn (~> 4) - version_sorter (1.1.0) + version_sorter (2.0.0) virtus (1.0.1) axiom-types (~> 0.0.5) coercible (~> 1.0) @@ -641,7 +678,9 @@ DEPENDENCIES better_errors binding_of_caller bootstrap-sass (~> 3.0) + brakeman browser + byebug cal-heatmap-rails (~> 0.0.1) capybara (~> 2.2.1) carrierwave @@ -667,10 +706,10 @@ DEPENDENCIES gemnasium-gitlab-service (~> 0.2) github-markup gitlab-flowdock-git-hook (~> 0.4.2) - gitlab-grack (~> 2.0.0.pre) - gitlab-linguist (~> 3.0.0) - gitlab_emoji (~> 0.0.1.1) - gitlab_git (= 7.0.0.rc14) + gitlab-grack (~> 2.0.0.rc2) + gitlab-linguist (~> 3.0.1) + gitlab_emoji (~> 0.1) + gitlab_git (= 7.1.0) gitlab_meta (= 7.0) gitlab_omniauth-ldap (= 1.2.0) gollum-lib (~> 4.0.0) @@ -682,7 +721,7 @@ DEPENDENCIES guard-spinach haml-rails hipchat (~> 1.4.0) - html-pipeline-gitlab (~> 0.1.0) + html-pipeline-gitlab (~> 0.1) httparty jasmine (= 2.0.2) jquery-atwho-rails (~> 0.3.3) @@ -700,6 +739,7 @@ DEPENDENCIES nprogress-rails octokit (= 3.7.0) omniauth (~> 1.1.3) + omniauth-bitbucket omniauth-github omniauth-gitlab omniauth-google-oauth2 @@ -724,7 +764,7 @@ DEPENDENCIES redcarpet (~> 3.1.2) redis-rails request_store - rspec-rails + rspec-rails (= 2.99) rubocop (= 0.28.0) rugments sanitize (~> 2.0) @@ -732,7 +772,6 @@ DEPENDENCIES sdoc seed-fu select2-rails - semantic-ui-sass (~> 1.8.0) settingslogic shoulda-matchers (~> 2.7.0) sidekiq (~> 3.3) @@ -741,9 +780,10 @@ DEPENDENCIES six slack-notifier (~> 1.0.0) slim + slowpoke spinach-rails - spring (= 1.1.3) - spring-commands-rspec (= 1.0.1) + spring (~> 1.3.1) + spring-commands-rspec (= 1.0.4) spring-commands-spinach (= 1.0.0) stamp state_machine diff --git a/PROCESS.md b/PROCESS.md index 5cc25de05a4..1b6b3e7d32d 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -71,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue ### Feature requests -Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the [feature request forum](http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. +Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the \[feature request forum\]\(http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. ### Issue report for old version diff --git a/README.md b/README.md index 393909ef7c0..0563ceca409 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,19 @@ - Each project can also have an issue tracker and a wiki - Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises - Completely free and open source (MIT Expat license) -- Powered by Ruby on Rails +- Powered by [Ruby on Rails](https://github.com/rails/rails) ## Editions There are two editions of GitLab. -GitLab [Community Edition](https://about.gitlab.com/features/) (CE) is available without any costs under an MIT license. +*GitLab [Community Edition](https://about.gitlab.com/features/) (CE)* is available without any costs under an MIT license. -GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users. +*GitLab Enterprise Edition (EE)* includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users. To get access to the EE and support please [become a subscriber](https://about.gitlab.com/pricing/). ## Canonical source -- The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. +The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. ## Code status @@ -33,8 +33,6 @@ To get access to the EE and support please [become a subscriber](https://about.g - [](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) -- [](https://www.pullreview.com/gitlab.gitlab.com/gitlab-org/gitlab-ce/reviews/master) - ## Website On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: @@ -48,42 +46,45 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a ## Requirements -- Ubuntu/Debian/CentOS/RHEL** +GitLab requires the following software: + +- Ubuntu/Debian/CentOS/RHEL - Ruby (MRI) 2.0 or 2.1 -- git 1.7.10+ -- redis 2.0+ +- Git 1.7.10+ +- Redis 2.0+ - MySQL or PostgreSQL -** More details are in the [requirements doc](doc/install/requirements.md). +Please see the [requirements documentation](doc/install/requirements.md) for system requirements and more information about the supported operating systems. ## Installation -Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/) for the various options. -Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm). -You can access new installation with the login `root` and password `5iveL!fe`, after login you are required to set a unique password. +The recommended way to install GitLab is using the provided [Omnibus packages](https://about.gitlab.com/downloads/). Compared to an installation from source, this is faster and less error prone. Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager. + +There are various other options to install GitLab, please refer to the [installation page on the GitLab website](https://about.gitlab.com/installation/) for more information. + +You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password. ## Third-party applications -There are a lot of applications and API wrappers for GitLab. -Find them [on our website](https://about.gitlab.com/applications/). +There are a lot of [third-party applications integrating with GitLab](https://about.gitlab.com/applications/). These include GUI Git clients, mobile applications and API wrappers for various languages. -## New versions +## GitLab release cycle -Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). +Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases are published when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). ## Upgrading -For updating the the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For manual installations there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update). +For updating the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For installations from source there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update) detailing all necessary commands to migrate to the next version. ## Install a development environment -We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). -If you do not use the GitLab Development Development kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone. +To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). +If you do not use the GitLab Development Kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone. One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file: cp config/unicorn.rb.example.development config/unicorn.rb -Instructions on how to start Gitlab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development). +Instructions on how to start GitLab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development). ## Documentation @@ -1 +1 @@ -7.8.0.pre +7.9.0.pre diff --git a/app/assets/images/authbuttons/bitbucket_32.png b/app/assets/images/authbuttons/bitbucket_32.png Binary files differnew file mode 100644 index 00000000000..27702eb973d --- /dev/null +++ b/app/assets/images/authbuttons/bitbucket_32.png diff --git a/app/assets/images/authbuttons/bitbucket_64.png b/app/assets/images/authbuttons/bitbucket_64.png Binary files differnew file mode 100644 index 00000000000..4b90a57bc7d --- /dev/null +++ b/app/assets/images/authbuttons/bitbucket_64.png diff --git a/app/assets/images/gitorious-logo-black.png b/app/assets/images/gitorious-logo-black.png Binary files differnew file mode 100644 index 00000000000..78f17a9af79 --- /dev/null +++ b/app/assets/images/gitorious-logo-black.png diff --git a/app/assets/images/gitorious-logo-blue.png b/app/assets/images/gitorious-logo-blue.png Binary files differnew file mode 100644 index 00000000000..4962cffba31 --- /dev/null +++ b/app/assets/images/gitorious-logo-blue.png diff --git a/app/assets/images/logo-black.png b/app/assets/images/logo-black.png Binary files differdeleted file mode 100644 index 49cdc16cacd..00000000000 --- a/app/assets/images/logo-black.png +++ /dev/null diff --git a/app/assets/images/logo-white.png b/app/assets/images/logo-white.png Binary files differindex 2299153caba..917bcfcb7e7 100644 --- a/app/assets/images/logo-white.png +++ b/app/assets/images/logo-white.png diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 9c97582e6dd..c7acde2afe5 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -16,6 +16,7 @@ #= require jquery.scrollTo #= require jquery.blockUI #= require jquery.turbolinks +#= require jquery.sticky-kit.min #= require turbolinks #= require autosave #= require bootstrap @@ -32,7 +33,6 @@ #= require nprogress #= require nprogress-turbolinks #= require dropzone -#= require semantic-ui/sidebar #= require mousetrap #= require mousetrap/pause #= require shortcuts @@ -115,7 +115,6 @@ if location.hash window.addEventListener "hashchange", shiftWindow $ -> - # Click a .one_click_select field, select the contents $(".one_click_select").on 'click', -> $(@).select() @@ -183,6 +182,8 @@ $ -> form = btn.closest("form") new ConfirmDangerModal(form, text) + new Aside() + (($) -> # Disable an element and add the 'disabled' Bootstrap class $.fn.extend disable: -> diff --git a/app/assets/javascripts/aside.js.coffee b/app/assets/javascripts/aside.js.coffee new file mode 100644 index 00000000000..85473101944 --- /dev/null +++ b/app/assets/javascripts/aside.js.coffee @@ -0,0 +1,17 @@ +class @Aside + constructor: -> + $(document).off "click", "a.show-aside" + $(document).on "click", 'a.show-aside', (e) -> + e.preventDefault() + btn = $(e.currentTarget) + icon = btn.find('i') + console.log('1') + + if icon.hasClass('fa-angle-left') + btn.parent().find('section').hide() + btn.parent().find('aside').fadeIn() + icon.removeClass('fa-angle-left').addClass('fa-angle-right') + else + btn.parent().find('aside').hide() + btn.parent().find('section').fadeIn() + icon.removeClass('fa-angle-right').addClass('fa-angle-left') diff --git a/app/assets/javascripts/autosave.js.coffee b/app/assets/javascripts/autosave.js.coffee index 3450f4b55f7..5d3fe81da74 100644 --- a/app/assets/javascripts/autosave.js.coffee +++ b/app/assets/javascripts/autosave.js.coffee @@ -14,7 +14,11 @@ class @Autosave restore: -> return unless window.localStorage? - text = window.localStorage.getItem @key + try + text = window.localStorage.getItem @key + catch + return + @field.val text if text?.length > 0 @field.trigger "input" @@ -23,11 +27,13 @@ class @Autosave text = @field.val() if text?.length > 0 - window.localStorage.setItem @key, text + try + window.localStorage.setItem @key, text else @reset() reset: -> return unless window.localStorage? - window.localStorage.removeItem @key
\ No newline at end of file + try + window.localStorage.removeItem @key diff --git a/app/assets/javascripts/blob/blob.js.coffee b/app/assets/javascripts/blob/blob.js.coffee index a5f15f80c5c..37a175fdbc7 100644 --- a/app/assets/javascripts/blob/blob.js.coffee +++ b/app/assets/javascripts/blob/blob.js.coffee @@ -26,7 +26,7 @@ class @BlobView unless isNaN first_line $("#tree-content-holder .highlight .line").removeClass("hll") $("#LC#{line}").addClass("hll") for line in [first_line..last_line] - $.scrollTo("#L#{first_line}") unless e? + $.scrollTo("#L#{first_line}", offset: -50) unless e? # parse selected lines from hash # always return first and last line (initialized to NaN) diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index 6914ca759f6..2e91a06daa8 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -15,7 +15,7 @@ class @EditBlob $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() - return + return false editModePanes = $(".js-edit-mode-pane") editModeLinks = $(".js-edit-mode a") diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index a6e27116b40..ab8f98715e8 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -15,7 +15,7 @@ class @NewBlob $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() - return + return false editor: -> return @editor diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index 70940e13858..19ea4ccc4cf 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -16,11 +16,8 @@ class @calendar subDomain: "day" range: 12 tooltip: true - domainDynamicDimension: false - colLimit: 4 label: position: "top" - domainMargin: 1 legend: [ 0 1 diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee index 6ef5a539b8f..00ee503ff16 100644 --- a/app/assets/javascripts/dashboard.js.coffee +++ b/app/assets/javascripts/dashboard.js.coffee @@ -1,30 +1,3 @@ class @Dashboard constructor: -> - @initSidebarTab() - - $(".dash-filter").keyup -> - terms = $(this).val() - uiBox = $(this).parents('.panel').first() - if terms == "" || terms == undefined - uiBox.find(".dash-list li").show() - else - uiBox.find(".dash-list li").each (index) -> - name = $(this).find(".filter-title").text() - - if name.toLowerCase().search(terms.toLowerCase()) == -1 - $(this).hide() - else - $(this).show() - - - - initSidebarTab: -> - key = "dashboard_sidebar_filter" - - # store selection in cookie - $('.dash-sidebar-tabs a').on 'click', (e) -> - $.cookie(key, $(e.target).attr('id')) - - # show tab from cookie - sidebar_filter = $.cookie(key) - $("#" + sidebar_filter).tab('show') if sidebar_filter + new ProjectsList() diff --git a/app/assets/javascripts/diff.js.coffee b/app/assets/javascripts/diff.js.coffee index 52b4208524f..05f5af42571 100644 --- a/app/assets/javascripts/diff.js.coffee +++ b/app/assets/javascripts/diff.js.coffee @@ -1,6 +1,7 @@ class @Diff UNFOLD_COUNT = 20 constructor: -> + $(document).off('click', '.js-unfold') $(document).on('click', '.js-unfold', (event) => target = $(event.target) unfoldBottom = target.hasClass('js-unfold-bottom') @@ -36,6 +37,8 @@ class @Diff ) ) + $('.diff-header').stick_in_parent(recalc_every: 1, offset_top: $('.navbar').height()) + lineNumbers: (line) -> return ([0, 0]) unless line.children().length lines = line.children().slice(0, 2) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 1643ca941ff..e1015a63d52 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -26,19 +26,23 @@ class Dispatcher new ZenMode() when 'projects:milestones:show' new Milestone() - when 'projects:milestones:new' + when 'projects:milestones:new', 'projects:milestones:edit' new ZenMode() when 'projects:issues:new','projects:issues:edit' GitLab.GfmAutoComplete.setup() shortcut_handler = new ShortcutsNavigation() new ZenMode() new DropzoneInput($('.issue-form')) + if page == 'projects:issues:new' + new IssuableForm($('.issue-form')) when 'projects:merge_requests:new', 'projects:merge_requests:edit' GitLab.GfmAutoComplete.setup() new Diff() shortcut_handler = new ShortcutsNavigation() new ZenMode() new DropzoneInput($('.merge-request-form')) + if page == 'projects:merge_requests:new' + new IssuableForm($('.merge-request-form')) when 'projects:merge_requests:show' new Diff() shortcut_handler = new ShortcutsIssueable() @@ -48,18 +52,27 @@ class Dispatcher new ZenMode() when 'projects:merge_requests:index' shortcut_handler = new ShortcutsNavigation() + MergeRequests.init() when 'dashboard:show' new Dashboard() new Activities() + when 'dashboard:projects:starred' + new Activities() + new ProjectsList() when 'projects:commit:show' new Commit() new Diff() + new ZenMode() shortcut_handler = new ShortcutsNavigation() when 'projects:commits:show' shortcut_handler = new ShortcutsNavigation() - when 'groups:show', 'projects:show' + when 'projects:show' + new Activities() + shortcut_handler = new ShortcutsNavigation() + when 'groups:show' new Activities() shortcut_handler = new ShortcutsNavigation() + new ProjectsList() when 'groups:members' new GroupMembers() new UsersSelect() diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index d98d5482937..06e9f0001ae 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -6,10 +6,10 @@ class @DropzoneInput divHover = "<div class=\"div-dropzone-hover\"></div>" divSpinner = "<div class=\"div-dropzone-spinner\"></div>" divAlert = "<div class=\"" + alertClass + "\"></div>" - iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>" + iconPaperclip = "<i class=\"fa fa-paperclip div-dropzone-icon\"></i>" iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>" btnAlert = "<button type=\"button\"" + alertAttr + ">×</button>" - project_image_path_upload = window.project_image_path_upload or null + project_uploads_path = window.project_uploads_path or null form_textarea = $(form).find("textarea.markdown-area") form_textarea.wrap "<div class=\"div-dropzone\"></div>" @@ -19,7 +19,7 @@ class @DropzoneInput form_dropzone = $(form).find('.div-dropzone') form_dropzone.parent().addClass "div-dropzone-wrapper" form_dropzone.append divHover - $(".div-dropzone-hover").append iconPicture + $(".div-dropzone-hover").append iconPaperclip form_dropzone.append divSpinner $(".div-dropzone-spinner").append iconSpinner $(".div-dropzone-spinner").css @@ -72,13 +72,12 @@ class @DropzoneInput form.find(".md-preview-holder").hide() dropzone = form_dropzone.dropzone( - url: project_image_path_upload + url: project_uploads_path dictDefaultMessage: "" clickable: true - paramName: "markdown_img" + paramName: "file" maxFilesize: 10 uploadMultiple: false - acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png" headers: "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") @@ -132,8 +131,10 @@ class @DropzoneInput child = $(dropzone[0]).children("textarea") - formatLink = (str) -> - "" + formatLink = (link) -> + text = "[#{link.alt}](#{link.url})" + text = "!#{text}" if link.is_image + text handlePaste = (event) -> pasteEvent = event.originalEvent @@ -177,9 +178,9 @@ class @DropzoneInput uploadFile = (item, filename) -> formData = new FormData() - formData.append "markdown_img", item, filename + formData.append "file", item, filename $.ajax - url: project_image_path_upload + url: project_uploads_path type: "POST" data: formData dataType: "json" @@ -233,5 +234,7 @@ class @DropzoneInput $(@).closest('.gfm-form').find('.div-dropzone').click() return - formatLink: (str) -> - "" + formatLink: (link) -> + text = "[#{link.alt}](#{link.url})" + text = "!#{text}" if link.is_image + text
\ No newline at end of file diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee index e0e7771ab20..be8d225e73b 100644 --- a/app/assets/javascripts/importer_status.js.coffee +++ b/app/assets/javascripts/importer_status.js.coffee @@ -16,20 +16,20 @@ class @ImporterStatus $(".js-import-all").click (event) => $(".js-add-to-import").each -> $(this).click() - + setAutoUpdate: -> setInterval (=> $.get @jobs_url, (data) => $.each data, (i, job) => job_item = $("#project_" + job.id) status_field = job_item.find(".job-status") - + if job.import_status == 'finished' job_item.removeClass("active").addClass("success") - status_field.html('<span class="cgreen"><i class="fa fa-check"></i> done</span>') + status_field.html('<span><i class="fa fa-check"></i> done</span>') else if job.import_status == 'started' status_field.html("<i class='fa fa-spinner fa-spin'></i> started") else status_field.html(job.import_status) - - ), 4000
\ No newline at end of file + + ), 4000 diff --git a/app/assets/javascripts/issuable_form.js.coffee b/app/assets/javascripts/issuable_form.js.coffee new file mode 100644 index 00000000000..abd58bcf978 --- /dev/null +++ b/app/assets/javascripts/issuable_form.js.coffee @@ -0,0 +1,28 @@ +class @IssuableForm + constructor: (@form) -> + @titleField = @form.find("input[name*='[title]']") + @descriptionField = @form.find("textarea[name*='[description]']") + + return unless @titleField.length && @descriptionField.length + + @initAutosave() + + @form.on "submit", @resetAutosave + @form.on "click", ".btn-cancel", @resetAutosave + + initAutosave: -> + new Autosave @titleField, [ + document.location.pathname, + document.location.search, + "title" + ] + + new Autosave @descriptionField, [ + document.location.pathname, + document.location.search, + "description" + ] + + resetAutosave: => + @titleField.data("autosave").reset() + @descriptionField.data("autosave").reset() diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index 45c248e6fb6..f2753170478 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -15,3 +15,10 @@ class @Issue "issue" updateTaskState ) + + $('.issue-details').waitForImages -> + $('.issuable-affix').affix offset: + top: -> + @top = $('.issue-details').outerHeight(true) + 25 + bottom: -> + @bottom = $('.footer').outerHeight(true) diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index 2499ad5ad80..40bb9e9cb0c 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -15,7 +15,7 @@ $(this).html totalIssues + 1 else $(this).html totalIssues - 1 - $("body").on "click", ".issues-filters .dropdown-menu a", -> + $("body").on "click", ".issues-other-filters .dropdown-menu a", -> $('.issues-list').block( message: null, overlayCSS: @@ -47,7 +47,7 @@ initSearch: -> @timer = null $("#issue_search").keyup -> - clearTimeout(@timer); + clearTimeout(@timer) @timer = setTimeout(Issues.filterResults, 500) filterResults: => @@ -77,9 +77,9 @@ ids.push $(value).attr("data-id") $("#update_issues_ids").val ids - $(".issues-filters").hide() + $(".issues-other-filters").hide() $(".issues_bulk_update").show() else $("#update_issues_ids").val [] $(".issues_bulk_update").hide() - $(".issues-filters").show() + $(".issues-other-filters").show() diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 5bcbd56852d..1fee9dc1892 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -20,6 +20,13 @@ class @MergeRequest if $("a.btn-close").length $("li.task-list-item input:checkbox").prop("disabled", false) + $('.merge-request-details').waitForImages -> + $('.issuable-affix').affix offset: + top: -> + @top = $('.merge-request-details').outerHeight(true) + 91 + bottom: -> + @bottom = $('.footer').outerHeight(true) + # Local jQuery finder $: (selector) -> this.$el.find(selector) @@ -89,6 +96,7 @@ class @MergeRequest this.$('.merge-request-tabs .diffs-tab').addClass 'active' this.loadDiff() unless @diffs_loaded this.$('.diffs').show() + $(".diff-header").trigger("sticky_kit:recalc") when 'commits' this.$('.merge-request-tabs .commits-tab').addClass 'active' this.$('.commits').show() @@ -117,7 +125,7 @@ class @MergeRequest loadDiff: (event) -> $.ajax type: 'GET' - url: this.$('.merge-request-tabs .diffs-tab a').attr('href') + url: this.$('.merge-request-tabs .diffs-tab a').attr('href') + ".json" beforeSend: => this.$('.mr-loading-status .loading').show() complete: => diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee index 9201c84c5ed..83434c1b9ba 100644 --- a/app/assets/javascripts/merge_requests.js.coffee +++ b/app/assets/javascripts/merge_requests.js.coffee @@ -1,8 +1,35 @@ # # * Filter merge requests # -@merge_requestsPage = -> - $('#assignee_id').select2() - $('#milestone_id').select2() - $('#milestone_id, #assignee_id').on 'change', -> - $(this).closest('form').submit() +@MergeRequests = + init: -> + MergeRequests.initSearch() + + # Make sure we trigger ajax request only after user stop typing + initSearch: -> + @timer = null + $("#issue_search").keyup -> + clearTimeout(@timer) + @timer = setTimeout(MergeRequests.filterResults, 500) + + filterResults: => + form = $("#issue_search_form") + search = $("#issue_search").val() + $('.merge-requests-holder').css("opacity", '0.5') + issues_url = form.attr('action') + '? '+ form.serialize() + + $.ajax + type: "GET" + url: form.attr('action') + data: form.serialize() + complete: -> + $('.merge-requests-holder').css("opacity", '1.0') + success: (data) -> + $('.merge-requests-holder').html(data.html) + # Change url so if user reload a page - search results are saved + History.replaceState {page: issues_url}, document.title, issues_url + MergeRequests.reload() + dataType: "json" + + reload: -> + $('#filter_issue_search').val($('#issue_search').val()) diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee index c42f31933d3..d644d50b669 100644 --- a/app/assets/javascripts/milestone.js.coffee +++ b/app/assets/javascripts/milestone.js.coffee @@ -49,6 +49,13 @@ class @Milestone data: data success: (data) -> if data.saved == true + if data.assignee_avatar_url + img_tag = $('<img/>') + img_tag.attr('src', data.assignee_avatar_url) + img_tag.addClass('avatar s16') + $(li).find('.assignee-icon').html(img_tag) + else + $(li).find('.assignee-icon').html('') $(li).effect 'highlight' else new Flash("Issue update failed", 'alert') diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 47c5ecdedf1..90e6fd6d154 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -39,9 +39,6 @@ class @Notes # reset main target form after submit $(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm - # attachment button - $(document).on "click", ".js-choose-note-attachment-button", @chooseNoteAttachment - # update the file name when an attachment is selected $(document).on "change", ".js-note-attachment-input", @updateFormAttachment @@ -73,11 +70,10 @@ class @Notes $(document).off "click", ".js-note-delete" $(document).off "click", ".js-note-attachment-delete" $(document).off "ajax:complete", ".js-main-target-form" - $(document).off "click", ".js-choose-note-attachment-button" $(document).off "click", ".js-discussion-reply-button" $(document).off "click", ".js-add-diff-note-button" $(document).off "visibilitychange" - $(document).off "keypress", @notes_forms + $(document).off "keydown", @notes_forms $(document).off "keyup", ".js-note-text" $(document).off "click", ".js-note-target-reopen" $(document).off "click", ".js-note-target-close" @@ -174,15 +170,6 @@ class @Notes form.find(".js-note-text").data("autosave").reset() ### - Called when clicking the "Choose File" button. - - Opens the file selection dialog. - ### - chooseNoteAttachment: -> - form = $(this).closest("form") - form.find(".js-note-attachment-input").click() - - ### Shows the main form and does some setup on it. Sets some hidden fields in the form. @@ -272,7 +259,7 @@ class @Notes note_li = $(".note-row-" + note.id) note_li.replaceWith(note.html) note_li.find('.note-edit-form').hide() - note_li.find('.note-text').show() + note_li.find('.note-body > .note-text').show() ### Called in response to clicking the edit note link @@ -284,7 +271,7 @@ class @Notes showEditForm: (e) -> e.preventDefault() note = $(this).closest(".note") - note.find(".note-text").hide() + note.find(".note-body > .note-text").hide() note.find(".note-header").hide() base_form = note.find(".note-edit-form") form = base_form.clone().insertAfter(base_form) @@ -311,7 +298,7 @@ class @Notes cancelEdit: (e) -> e.preventDefault() note = $(this).closest(".note") - note.find(".note-text").show() + note.find(".note-body > .note-text").show() note.find(".note-header").show() note.find(".current-note-edit-form").remove() @@ -345,7 +332,7 @@ class @Notes removeAttachment: -> note = $(this).closest(".note") note.find(".note-attachment").remove() - note.find(".note-text").show() + note.find(".note-body > .note-text").show() note.find(".js-note-attachment-delete").hide() note.find(".note-edit-form").hide() diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 5a9cc66c8f0..eb8c1fa1426 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -16,5 +16,11 @@ class @Project $('.hide-no-ssh-message').on 'click', (e) -> path = '/' $.cookie('hide_no_ssh_message', 'false', { path: path }) - $(@).parents('.no-ssh-key-message').hide() + $(@).parents('.no-ssh-key-message').remove() + e.preventDefault() + + $('.hide-no-password-message').on 'click', (e) -> + path = '/' + $.cookie('hide_no_password_message', 'false', { path: path }) + $(@).parents('.no-password-message').remove() e.preventDefault() diff --git a/app/assets/javascripts/project_show.js.coffee b/app/assets/javascripts/project_show.js.coffee index d0eaaad92b8..6828ae471e5 100644 --- a/app/assets/javascripts/project_show.js.coffee +++ b/app/assets/javascripts/project_show.js.coffee @@ -6,7 +6,7 @@ class @ProjectShow new Flash('Star toggle failed. Try again later.', 'alert') $("a[data-toggle='tab']").on "shown.bs.tab", (e) -> - $.cookie "default_view", $(e.target).attr("href"), { expires: 30 } + $.cookie "default_view", $(e.target).attr("href"), { expires: 30, path: '/' } defaultView = $.cookie("default_view") if defaultView diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee index 7fb33926096..e22c7c11f1c 100644 --- a/app/assets/javascripts/project_users_select.js.coffee +++ b/app/assets/javascripts/project_users_select.js.coffee @@ -11,14 +11,15 @@ class @ProjectUsersSelect Api.projectUsers project_id, query.term, (users) -> data = { results: users } - nullUser = { - name: 'Unassigned', - avatar: null, - username: 'none', - id: '' - } - - data.results.unshift(nullUser) + if query.term.length == 0 + nullUser = { + name: 'Unassigned', + avatar: null, + username: 'none', + id: -1 + } + + data.results.unshift(nullUser) query.callback(data) diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee new file mode 100644 index 00000000000..c0e36d1ccc5 --- /dev/null +++ b/app/assets/javascripts/projects_list.js.coffee @@ -0,0 +1,24 @@ +class @ProjectsList + constructor: -> + $(".projects-list .js-expand").on 'click', (e) -> + e.preventDefault() + list = $(this).closest('.projects-list') + list.find("li").show() + list.find("li.bottom").hide() + + $(".projects-list-filter").keyup -> + terms = $(this).val() + uiBox = $(this).closest('.panel') + if terms == "" || terms == undefined + uiBox.find(".projects-list li").show() + else + uiBox.find(".projects-list li").each (index) -> + name = $(this).find(".filter-title").text() + + if name.toLowerCase().search(terms.toLowerCase()) == -1 + $(this).hide() + else + $(this).show() + uiBox.find(".projects-list li.bottom").hide() + + diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee index 5013bcdacd0..7febcba0e94 100644 --- a/app/assets/javascripts/sidebar.js.coffee +++ b/app/assets/javascripts/sidebar.js.coffee @@ -1,30 +1,3 @@ -responsive_resize = -> - current_width = $(window).width() - if current_width < 985 - $('.responsive-side').addClass("ui right wide sidebar") - else - $('.responsive-side').removeClass("ui right wide sidebar") - -$ -> - # Depending on window size, set the sidebar offscreen. - responsive_resize() - - $('.sidebar-expand-button').click -> - $('.ui.sidebar') - .sidebar({overlay: true}) - .sidebar('toggle') - - # Hide sidebar on click outside of sidebar - $(document).mouseup (e) -> - container = $(".ui.sidebar") - container.sidebar "hide" if not container.is(e.target) and container.has(e.target).length is 0 - return - -# On resize, check if sidebar should be offscreen. -$(window).resize -> - responsive_resize() - return - $(document).on("click", '.toggle-nav-collapse', (e) -> e.preventDefault() collapsed = 'page-sidebar-collapsed' diff --git a/app/assets/javascripts/user.js.coffee b/app/assets/javascripts/user.js.coffee index 8a2e2421c2e..d0d81f96921 100644 --- a/app/assets/javascripts/user.js.coffee +++ b/app/assets/javascripts/user.js.coffee @@ -1,3 +1,4 @@ class @User constructor: -> $('.profile-groups-avatars').tooltip("placement": "top") + new ProjectsList() diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 8f63a7fee64..015ff2ce4ec 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -11,12 +11,17 @@ *= require cal-heatmap */ -@import "main/*"; + +@import "base/variables"; +@import "base/mixins"; +@import "base/layout"; + /** * Customized Twitter bootstrap */ -@import 'gl_bootstrap'; +@import 'base/gl_variables'; +@import 'base/gl_bootstrap'; /** * NProgress load bar css @@ -39,7 +44,7 @@ * Page specific styles (issues, projects etc): */ -@import "sections/*"; +@import "pages/*"; /** * Code highlight @@ -55,8 +60,3 @@ * Styles for JS behaviors. */ @import "behaviors.scss"; - -/** -* Styles for responsive sidebar -*/ -@import "semantic-ui/modules/sidebar"; diff --git a/app/assets/stylesheets/gl_bootstrap.scss b/app/assets/stylesheets/base/gl_bootstrap.scss index 6efa56544a5..16581e9ebf2 100644 --- a/app/assets/stylesheets/gl_bootstrap.scss +++ b/app/assets/stylesheets/base/gl_bootstrap.scss @@ -3,11 +3,6 @@ * */ -$font-size-base: 13px !default; -$nav-pills-active-link-hover-bg: $bg_primary; -$pagination-active-bg: $bg_primary; -$list-group-active-bg: $bg_primary; - // Core variables and mixins @import "bootstrap/variables"; @import "bootstrap/mixins"; @@ -23,6 +18,7 @@ $list-group-active-bg: $bg_primary; @import "bootstrap/grid"; @import "bootstrap/tables"; @import "bootstrap/forms"; +@import "bootstrap/buttons"; // Components @import "bootstrap/component-animations"; @@ -134,10 +130,6 @@ $list-group-active-bg: $bg_primary; } } } - - &.nav-small-tabs > li > a { - padding: 6px 9px; - } } .nav-tabs > li > a, @@ -145,61 +137,6 @@ $list-group-active-bg: $bg_primary; color: #666; } -.nav-compact > li > a { - padding: 6px 12px; -} - -.nav-small > li > a { - padding: 3px 5px; - font-size: 12px; -} - - -/* - * Callouts from Bootstrap3 docs - * - * Not quite alerts, but custom and helpful notes for folks reading the docs. - * Requires a base and modifier class. - */ - -/* Common styles for all types */ -.bs-callout { - margin: 20px 0; - padding: 20px; - border-left: 3px solid #eee; - color: #666; - background: #f9f9f9; -} -.bs-callout h4 { - margin-top: 0; - margin-bottom: 5px; -} -.bs-callout p:last-child { - margin-bottom: 0; -} - -/* Variations */ -.bs-callout-danger { - background-color: #fdf7f7; - border-color: #eed3d7; - color: #b94a48; -} -.bs-callout-warning { - background-color: #faf8f0; - border-color: #faebcc; - color: #8a6d3b; -} -.bs-callout-info { - background-color: #f4f8fa; - border-color: #bce8f1; - color: #34789a; -} -.bs-callout-success { - background-color: #dff0d8; - border-color: #5cA64d; - color: #3c763d; -} - /** * fix to keep tooltips position in top navigation bar * @@ -214,16 +151,13 @@ $list-group-active-bg: $bg_primary; * */ .panel { - @include border-radius(0px); - .panel-heading { - @include border-radius(0px); font-size: 14px; line-height: 18px; .panel-head-actions { position: relative; - top: -7px; + top: -6px; float: right; } } @@ -256,40 +190,57 @@ $list-group-active-bg: $bg_primary; } } -.panel-default { - .panel-heading { - background-color: #EEE; +.alert { + a { + @extend .alert-link; + color: #fff; + text-decoration: underline; } } -.panel-danger { - @include panel-colored; - .panel-heading { - color: $border_danger; - border-color: $border_danger; - } +// Typography ================================================================= + +.text-primary, +.text-primary:hover { + color: $brand-primary; } -.panel-success { - @include panel-colored; - .panel-heading { - color: $border_success; - border-color: $border_success; - } +.text-success, +.text-success:hover { + color: $brand-success; } -.panel-primary { - @include panel-colored; - .panel-heading { - color: $border_primary; - border-color: $border_primary; - } +.text-danger, +.text-danger:hover { + color: $brand-danger; } -.panel-warning { - @include panel-colored; - .panel-heading { - color: $border_warning; - border-color: $border_warning; +.text-warning, +.text-warning:hover { + color: $brand-warning; +} + +.text-info, +.text-info:hover { + color: $brand-info; +} + +// Tables ===================================================================== + +table.table { + .dropdown-menu a { + text-decoration: none; + } + + .success, + .warning, + .danger, + .info { + color: #fff; + + a:not(.btn) { + text-decoration: underline; + color: #fff; + } } } diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss new file mode 100644 index 00000000000..ea230646a89 --- /dev/null +++ b/app/assets/stylesheets/base/gl_variables.scss @@ -0,0 +1,870 @@ +// Override Bootstrap variables here (defaults from bootstrap-sass v3.3.3): + +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +// $gray-base: #000 +// $gray-darker: lighten($gray-base, 13.5%) // #222 +// $gray-dark: lighten($gray-base, 20%) // #333 +// $gray: lighten($gray-base, 33.5%) // #555 +// $gray-light: lighten($gray-base, 46.7%) // #777 +// $gray-lighter: lighten($gray-base, 93.5%) // #eee +$gray-base: #000; +$gray-darker: lighten($gray-base, 13.5%); // #222 +$gray-dark: #7b8a8b; // #333 +$gray: #95a5a6; // #555 +$gray-light: #b4bcc2; // #999 +$gray-lighter: #ecf0f1; // #eee + +$brand-primary: $gl-primary; +$brand-success: $gl-success; +$brand-info: $gl-info; +$brand-warning: $gl-warning; +$brand-danger: $gl-danger; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for `<body>`. +// $body-bg: #fff +//** Global text color on `<body>`. +$text-color: $brand-primary; + +//** Global textual link color. +$link-color: $gl-link-color; +//** Link hover color set via `darken()` function. +// $link-hover-color: darken($link-color, 15%) +//** Link hover decoration. +// $link-hover-decoration: underline + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +// $font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif +// $font-family-serif: Georgia, "Times New Roman", Times, serif +//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`. +// $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace +// $font-family-base: $font-family-sans-serif + +$font-size-base: $gl-font-size; +// $font-size-large: ceil(($font-size-base * 1.25)) // ~18px +// $font-size-small: ceil(($font-size-base * 0.85)) // ~12px + +// $font-size-h1: floor(($font-size-base * 2.6)) // ~36px +// $font-size-h2: floor(($font-size-base * 2.15)) // ~30px +// $font-size-h3: ceil(($font-size-base * 1.7)) // ~24px +// $font-size-h4: ceil(($font-size-base * 1.25)) // ~18px +// $font-size-h5: $font-size-base +// $font-size-h6: ceil(($font-size-base * 0.85)) // ~12px + +//** Unit-less `line-height` for use in components like buttons. +// $line-height-base: 1.428571429 // 20/14 +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc. +// $line-height-computed: floor(($font-size-base * $line-height-base)) // ~20px + +//** By default, this inherits from the `<body>`. +// $headings-font-family: inherit +// $headings-font-weight: 500 +// $headings-line-height: 1.1 +// $headings-color: inherit + + +//== Iconography +// +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower. + +//** Load fonts from this directory. + +// [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path. +// [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths. +// $icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") + +//** File name for all font files. +// $icon-font-name: "glyphicons-halflings-regular" +//** Element ID within SVG icon file. +// $icon-font-svg-id: "glyphicons_halflingsregular" + + +//== Components +// +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). + +$padding-base-vertical: 6px; +$padding-base-horizontal: 14px; + +// $padding-large-vertical: 10px +// $padding-large-horizontal: 16px + +// $padding-small-vertical: 5px +// $padding-small-horizontal: 10px + +// $padding-xs-vertical: 1px +// $padding-xs-horizontal: 5px + +// $line-height-large: 1.3333333 // extra decimals for Win 8.1 Chrome +// $line-height-small: 1.5 + +// $border-radius-base: 4px +// $border-radius-large: 6px +// $border-radius-small: 3px + +//** Global color for active items (e.g., navs or dropdowns). +// $component-active-color: #fff +//** Global background color for active items (e.g., navs or dropdowns). +// $component-active-bg: $brand-primary + +//** Width of the `border` for generating carets that indicator dropdowns. +// $caret-width-base: 4px +//** Carets increase slightly in size for larger components. +// $caret-width-large: 5px + + +//== Tables +// +//## Customizes the `.table` component with basic values, each used across all table variations. + +//** Padding for `<th>`s and `<td>`s. +// $table-cell-padding: 8px +//** Padding for cells in `.table-condensed`. +// $table-condensed-cell-padding: 5px + +//** Default background color used for all tables. +// $table-bg: transparent +//** Background color used for `.table-striped`. +// $table-bg-accent: #f9f9f9 +//** Background color used for `.table-hover`. +// $table-bg-hover: #f5f5f5 +// $table-bg-active: $table-bg-hover + +//** Border color for table and cell borders. +// $table-border-color: #ddd + + +//== Buttons +// +//## For each of Bootstrap's buttons, define text, background and border color. + +// $btn-font-weight: normal + +// $btn-default-color: #333 +// $btn-default-bg: #fff +// $btn-default-border: #ccc + +// $btn-primary-color: #fff +// $btn-primary-bg: $brand-primary +// $btn-primary-border: darken($btn-primary-bg, 5%) + +// $btn-success-color: #fff +// $btn-success-bg: $brand-success +// $btn-success-border: darken($btn-success-bg, 5%) + +// $btn-info-color: #fff +// $btn-info-bg: $brand-info +// $btn-info-border: darken($btn-info-bg, 5%) + +// $btn-warning-color: #fff +// $btn-warning-bg: $brand-warning +// $btn-warning-border: darken($btn-warning-bg, 5%) + +// $btn-danger-color: #fff +// $btn-danger-bg: $brand-danger +// $btn-danger-border: darken($btn-danger-bg, 5%) + +// $btn-link-disabled-color: $gray-light + + +//== Forms +// +//## + +//** `<input>` background color +// $input-bg: #fff +//** `<input disabled>` background color +// $input-bg-disabled: $gray-lighter + +//** Text color for `<input>`s +$input-color: $text-color; +//** `<input>` border color +$input-border: #dce4ec; + +// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4 +//** Default `.form-control` border radius +// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS. +// $input-border-radius: $border-radius-base +//** Large `.form-control` border radius +// $input-border-radius-large: $border-radius-large +//** Small `.form-control` border radius +// $input-border-radius-small: $border-radius-small + +//** Border color for inputs on focus +$input-border-focus: $brand-info; + +//** Placeholder text color +// $input-color-placeholder: #999 + +//** Default `.form-control` height +// $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) +//** Large `.form-control` height +// $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) +//** Small `.form-control` height +// $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) + +$legend-color: $text-color; +// $legend-border-color: #e5e5e5 + +//** Background color for textual input addons +// $input-group-addon-bg: $gray-lighter +//** Border color for textual input addons +// $input-group-addon-border-color: $input-border + +//** Disabled cursor for form controls and buttons. +// $cursor-disabled: not-allowed + + +//== Dropdowns +// +//## Dropdown menu container and contents. + +//** Background for the dropdown menu. +// $dropdown-bg: #fff +//** Dropdown menu `border-color`. +// $dropdown-border: rgba(0,0,0,.15) +//** Dropdown menu `border-color` **for IE8**. +// $dropdown-fallback-border: #ccc +//** Divider color for between dropdown items. +// $dropdown-divider-bg: #e5e5e5 + +//** Dropdown link text color. +// $dropdown-link-color: $gray-dark +//** Hover color for dropdown links. +// $dropdown-link-hover-color: darken($gray-dark, 5%) +//** Hover background for dropdown links. +// $dropdown-link-hover-bg: #f5f5f5 + +//** Active dropdown menu item text color. +// $dropdown-link-active-color: $component-active-color +//** Active dropdown menu item background color. +// $dropdown-link-active-bg: $component-active-bg + +//** Disabled dropdown menu item background color. +// $dropdown-link-disabled-color: $gray-light + +//** Text color for headers within dropdown menus. +// $dropdown-header-color: $gray-light + +//** Deprecated `$dropdown-caret-color` as of v3.1.0 +// $dropdown-caret-color: #000 + + +//-- Z-index master list +// +// Warning: Avoid customizing these values. They're used for a bird's eye view +// of components dependent on the z-axis and are designed to all work together. +// +// Note: These variables are not generated into the Customizer. + +// $zindex-navbar: 1000 +// $zindex-dropdown: 1000 +// $zindex-popover: 1060 +// $zindex-tooltip: 1070 +// $zindex-navbar-fixed: 1030 +// $zindex-modal: 1040 + + +//== Media queries breakpoints +// +//## Define the breakpoints at which your layout will change, adapting to different screen sizes. + +// Extra small screen / phone +//** Deprecated `$screen-xs` as of v3.0.1 +// $screen-xs: 480px +//** Deprecated `$screen-xs-min` as of v3.2.0 +// $screen-xs-min: $screen-xs +//** Deprecated `$screen-phone` as of v3.0.1 +// $screen-phone: $screen-xs-min + +// Small screen / tablet +//** Deprecated `$screen-sm` as of v3.0.1 +// $screen-sm: 768px +// $screen-sm-min: $screen-sm +//** Deprecated `$screen-tablet` as of v3.0.1 +// $screen-tablet: $screen-sm-min + +// Medium screen / desktop +//** Deprecated `$screen-md` as of v3.0.1 +// $screen-md: 992px +// $screen-md-min: $screen-md +//** Deprecated `$screen-desktop` as of v3.0.1 +// $screen-desktop: $screen-md-min + +// Large screen / wide desktop +//** Deprecated `$screen-lg` as of v3.0.1 +// $screen-lg: 1200px +// $screen-lg-min: $screen-lg +//** Deprecated `$screen-lg-desktop` as of v3.0.1 +// $screen-lg-desktop: $screen-lg-min + +// So media queries don't overlap when required, provide a maximum +// $screen-xs-max: ($screen-sm-min - 1) +// $screen-sm-max: ($screen-md-min - 1) +// $screen-md-max: ($screen-lg-min - 1) + + +//== Grid system +// +//## Define your custom responsive grid. + +//** Number of columns in the grid. +// $grid-columns: 12 +//** Padding between columns. Gets divided in half for the left and right. +// $grid-gutter-width: 30px +// Navbar collapse +//** Point at which the navbar becomes uncollapsed. +// $grid-float-breakpoint: $screen-sm-min +//** Point at which the navbar begins collapsing. +// $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) + + +//== Container sizes +// +//## Define the maximum width of `.container` for different screen sizes. + +// Small screen / tablet +// $container-tablet: (720px + $grid-gutter-width) +//** For `$screen-sm-min` and up. +// $container-sm: $container-tablet + +// Medium screen / desktop +// $container-desktop: (940px + $grid-gutter-width) +//** For `$screen-md-min` and up. +// $container-md: $container-desktop + +// Large screen / wide desktop +// $container-large-desktop: (1140px + $grid-gutter-width) +//** For `$screen-lg-min` and up. +// $container-lg: $container-large-desktop + + +//== Navbar +// +//## + +// Basics of a navbar +// $navbar-height: 50px +// $navbar-margin-bottom: $line-height-computed +// $navbar-border-radius: $border-radius-base +// $navbar-padding-horizontal: floor(($grid-gutter-width / 2)) +// $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) +// $navbar-collapse-max-height: 340px + +// $navbar-default-color: #777 +// $navbar-default-bg: #f8f8f8 +// $navbar-default-border: darken($navbar-default-bg, 6.5%) + +// Navbar links +// $navbar-default-link-color: #777 +// $navbar-default-link-hover-color: #333 +// $navbar-default-link-hover-bg: transparent +// $navbar-default-link-active-color: #555 +// $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%) +// $navbar-default-link-disabled-color: #ccc +// $navbar-default-link-disabled-bg: transparent + +// Navbar brand label +// $navbar-default-brand-color: $navbar-default-link-color +// $navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%) +// $navbar-default-brand-hover-bg: transparent + +// Navbar toggle +// $navbar-default-toggle-hover-bg: #ddd +// $navbar-default-toggle-icon-bar-bg: #888 +// $navbar-default-toggle-border-color: #ddd + + +// Inverted navbar +// Reset inverted navbar basics +// $navbar-inverse-color: lighten($gray-light, 15%) +// $navbar-inverse-bg: #222 +// $navbar-inverse-border: darken($navbar-inverse-bg, 10%) + +// Inverted navbar links +// $navbar-inverse-link-color: lighten($gray-light, 15%) +// $navbar-inverse-link-hover-color: #fff +// $navbar-inverse-link-hover-bg: transparent +// $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color +// $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%) +// $navbar-inverse-link-disabled-color: #444 +// $navbar-inverse-link-disabled-bg: transparent + +// Inverted navbar brand label +// $navbar-inverse-brand-color: $navbar-inverse-link-color +// $navbar-inverse-brand-hover-color: #fff +// $navbar-inverse-brand-hover-bg: transparent + +// Inverted navbar toggle +// $navbar-inverse-toggle-hover-bg: #333 +// $navbar-inverse-toggle-icon-bar-bg: #fff +// $navbar-inverse-toggle-border-color: #333 + + +//== Navs +// +//## + +//=== Shared nav styles +// $nav-link-padding: 10px 15px +// $nav-link-hover-bg: $gray-lighter + +// $nav-disabled-link-color: $gray-light +// $nav-disabled-link-hover-color: $gray-light + +//== Tabs +// $nav-tabs-border-color: #ddd + +// $nav-tabs-link-hover-border-color: $gray-lighter + +// $nav-tabs-active-link-hover-bg: $body-bg +// $nav-tabs-active-link-hover-color: $gray +// $nav-tabs-active-link-hover-border-color: #ddd + +// $nav-tabs-justified-link-border-color: #ddd +// $nav-tabs-justified-active-link-border-color: $body-bg + +//== Pills +// $nav-pills-border-radius: $border-radius-base +// $nav-pills-active-link-hover-bg: $component-active-bg +// $nav-pills-active-link-hover-color: $component-active-color + + +//== Pagination +// +//## + +$pagination-color: #fff; +$pagination-bg: $brand-success; +$pagination-border: transparent; + +$pagination-hover-color: #fff; +$pagination-hover-bg: darken($brand-success, 15%); +$pagination-hover-border: transparent; + +$pagination-active-color: #fff; +$pagination-active-bg: darken($brand-success, 15%); +$pagination-active-border: transparent; + +$pagination-disabled-color: #b4bcc2; +$pagination-disabled-bg: lighten($brand-success, 15%); +$pagination-disabled-border: transparent; + + +//== Pager +// +//## + +// $pager-bg: $pagination-bg +// $pager-border: $pagination-border +// $pager-border-radius: 15px + +// $pager-hover-bg: $pagination-hover-bg + +// $pager-active-bg: $pagination-active-bg +// $pager-active-color: $pagination-active-color + +// $pager-disabled-color: $pagination-disabled-color + + +//== Jumbotron +// +//## + +// $jumbotron-padding: 30px +// $jumbotron-color: inherit +// $jumbotron-bg: $gray-lighter +// $jumbotron-heading-color: inherit +// $jumbotron-font-size: ceil(($font-size-base * 1.5)) + + +//== Form states and alerts +// +//## Define colors for form feedback states and, by default, alerts. + + +$state-success-text: #fff; +$state-success-bg: $brand-success; +$state-success-border: $brand-success; + +$state-info-text: #fff; +$state-info-bg: $brand-info; +$state-info-border: $brand-info; + +$state-warning-text: #fff; +$state-warning-bg: $brand-warning; +$state-warning-border: $brand-warning; + +$state-danger-text: #fff; +$state-danger-bg: $brand-danger; +$state-danger-border: $brand-danger; + + +//== Tooltips +// +//## + +//** Tooltip max width +// $tooltip-max-width: 200px +//** Tooltip text color +// $tooltip-color: #fff +//** Tooltip background color +// $tooltip-bg: #000 +// $tooltip-opacity: .9 + +//** Tooltip arrow width +// $tooltip-arrow-width: 5px +//** Tooltip arrow color +// $tooltip-arrow-color: $tooltip-bg + + +//== Popovers +// +//## + +//** Popover body background color +// $popover-bg: #fff +//** Popover maximum width +// $popover-max-width: 276px +//** Popover border color +// $popover-border-color: rgba(0,0,0,.2) +//** Popover fallback border color +// $popover-fallback-border-color: #ccc + +//** Popover title background color +// $popover-title-bg: darken($popover-bg, 3%) + +//** Popover arrow width +// $popover-arrow-width: 10px +//** Popover arrow color +// $popover-arrow-color: $popover-bg + +//** Popover outer arrow width +// $popover-arrow-outer-width: ($popover-arrow-width + 1) +//** Popover outer arrow color +// $popover-arrow-outer-color: fade_in($popover-border-color, 0.05) +//** Popover outer arrow fallback color +// $popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%) + + +//== Labels +// +//## + +//** Default label background color +// $label-default-bg: $gray-light +//** Primary label background color +// $label-primary-bg: $brand-primary +//** Success label background color +// $label-success-bg: $brand-success +//** Info label background color +// $label-info-bg: $brand-info +//** Warning label background color +// $label-warning-bg: $brand-warning +//** Danger label background color +// $label-danger-bg: $brand-danger + +//** Default label text color +// $label-color: #fff +//** Default text color of a linked label +// $label-link-hover-color: #fff + + +//== Modals +// +//## + +//** Padding applied to the modal body +// $modal-inner-padding: 15px + +//** Padding applied to the modal title +// $modal-title-padding: 15px +//** Modal title line-height +// $modal-title-line-height: $line-height-base + +//** Background color of modal content area +// $modal-content-bg: #fff +//** Modal content border color +// $modal-content-border-color: rgba(0,0,0,.2) +//** Modal content border color **for IE8** +// $modal-content-fallback-border-color: #999 + +//** Modal backdrop background color +// $modal-backdrop-bg: #000 +//** Modal backdrop opacity +// $modal-backdrop-opacity: .5 +//** Modal header border color +// $modal-header-border-color: #e5e5e5 +//** Modal footer border color +// $modal-footer-border-color: $modal-header-border-color + +// $modal-lg: 900px +// $modal-md: 600px +// $modal-sm: 300px + + +//== Alerts +// +//## Define alert colors, border radius, and padding. + +// $alert-padding: 15px +$alert-border-radius: 0; +// $alert-link-font-weight: bold + +// $alert-success-bg: $state-success-bg +// $alert-success-text: $state-success-text +// $alert-success-border: $state-success-border + +// $alert-info-bg: $state-info-bg +// $alert-info-text: $state-info-text +// $alert-info-border: $state-info-border + +// $alert-warning-bg: $state-warning-bg +// $alert-warning-text: $state-warning-text +// $alert-warning-border: $state-warning-border + +// $alert-danger-bg: $state-danger-bg +// $alert-danger-text: $state-danger-text +// $alert-danger-border: $state-danger-border + + +//== Progress bars +// +//## + +//** Background color of the whole progress component +// $progress-bg: #f5f5f5 +//** Progress bar text color +// $progress-bar-color: #fff +//** Variable for setting rounded corners on progress bar. +// $progress-border-radius: $border-radius-base + +//** Default progress bar color +// $progress-bar-bg: $brand-primary +//** Success progress bar color +// $progress-bar-success-bg: $brand-success +//** Warning progress bar color +// $progress-bar-warning-bg: $brand-warning +//** Danger progress bar color +// $progress-bar-danger-bg: $brand-danger +//** Info progress bar color +// $progress-bar-info-bg: $brand-info + + +//== List group +// +//## + +//** Background color on `.list-group-item` +// $list-group-bg: #fff +//** `.list-group-item` border color +// $list-group-border: #ddd +//** List group border radius +// $list-group-border-radius: $border-radius-base + +//** Background color of single list items on hover +// $list-group-hover-bg: #f5f5f5 +//** Text color of active list items +// $list-group-active-color: $component-active-color +//** Background color of active list items +// $list-group-active-bg: $component-active-bg +//** Border color of active list elements +// $list-group-active-border: $list-group-active-bg +//** Text color for content within active list items +// $list-group-active-text-color: lighten($list-group-active-bg, 40%) + +//** Text color of disabled list items +// $list-group-disabled-color: $gray-light +//** Background color of disabled list items +// $list-group-disabled-bg: $gray-lighter +//** Text color for content within disabled list items +// $list-group-disabled-text-color: $list-group-disabled-color + +// $list-group-link-color: #555 +// $list-group-link-hover-color: $list-group-link-color +// $list-group-link-heading-color: #333 + + +//== Panels +// +//## + +// $panel-bg: #fff +// $panel-body-padding: 15px +// $panel-heading-padding: 10px 15px +// $panel-footer-padding: $panel-heading-padding +$panel-border-radius: 0; + +//** Border color for elements within panels +// $panel-inner-border: #ddd +// $panel-footer-bg: #f5f5f5 + +$panel-default-text: $text-color; +// $panel-default-border: #ddd +// $panel-default-heading-bg: #f5f5f5 + +// $panel-primary-text: #fff +// $panel-primary-border: $brand-primary +// $panel-primary-heading-bg: $brand-primary + +// $panel-success-text: $state-success-text +// $panel-success-border: $state-success-border +// $panel-success-heading-bg: $state-success-bg + +// $panel-info-text: $state-info-text +// $panel-info-border: $state-info-border +// $panel-info-heading-bg: $state-info-bg + +// $panel-warning-text: $state-warning-text +// $panel-warning-border: $state-warning-border +// $panel-warning-heading-bg: $state-warning-bg + +// $panel-danger-text: $state-danger-text +// $panel-danger-border: $state-danger-border +// $panel-danger-heading-bg: $state-danger-bg + + +//== Thumbnails +// +//## + +//** Padding around the thumbnail image +// $thumbnail-padding: 4px +//** Thumbnail background color +// $thumbnail-bg: $body-bg +//** Thumbnail border color +// $thumbnail-border: #ddd +//** Thumbnail border radius +// $thumbnail-border-radius: $border-radius-base + +//** Custom text color for thumbnail captions +// $thumbnail-caption-color: $text-color +//** Padding around the thumbnail caption +// $thumbnail-caption-padding: 9px + + +//== Wells +// +//## + +$well-bg: $gray-lighter; +$well-border: transparent; + + +//== Badges +// +//## + +// $badge-color: #fff +//** Linked badge text color on hover +// $badge-link-hover-color: #fff +// $badge-bg: $gray-light + +//** Badge text color in active nav link +// $badge-active-color: $link-color +//** Badge background color in active nav link +// $badge-active-bg: #fff + +// $badge-font-weight: bold +// $badge-line-height: 1 +// $badge-border-radius: 10px + + +//== Breadcrumbs +// +//## + +// $breadcrumb-padding-vertical: 8px +// $breadcrumb-padding-horizontal: 15px +//** Breadcrumb background color +// $breadcrumb-bg: #f5f5f5 +//** Breadcrumb text color +// $breadcrumb-color: #ccc +//** Text color of current page in the breadcrumb +// $breadcrumb-active-color: $gray-light +//** Textual separator for between breadcrumb elements +// $breadcrumb-separator: "/" + + +//== Carousel +// +//## + +// $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6) + +// $carousel-control-color: #fff +// $carousel-control-width: 15% +// $carousel-control-opacity: .5 +// $carousel-control-font-size: 20px + +// $carousel-indicator-active-bg: #fff +// $carousel-indicator-border-color: #fff + +// $carousel-caption-color: #fff + + +//== Close +// +//## + +// $close-font-weight: bold +// $close-color: #000 +// $close-text-shadow: 0 1px 0 #fff + + +//== Code +// +//## + +$code-color: #c7254e; +$code-bg: #f9f2f4; + +$kbd-color: #fff; +$kbd-bg: #333; + +$pre-bg: $gray-lighter; +$pre-color: $text-color; +$pre-border-color: #ccc; +// $pre-scrollable-max-height: 340px + + +//== Type +// +//## + +//** Horizontal offset for forms and lists. +// $component-offset-horizontal: 180px +//** Text muted color +// $text-muted: $gray-light +//** Abbreviations and acronyms border color +// $abbr-border-color: $gray-light +//** Headings small color +$headings-small-color: $gray-dark; +//** Blockquote small color +// $blockquote-small-color: $gray-light +//** Blockquote font size +// $blockquote-font-size: ($font-size-base * 1.25) +//** Blockquote border color +// $blockquote-border-color: $gray-lighter +//** Page header border color +// $page-header-border-color: $gray-lighter +//** Width of horizontal description list titles +// $dl-horizontal-offset: $component-offset-horizontal +//** Horizontal line color. +// $hr-border: $gray-lighter diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/base/layout.scss index 1085e68b7d4..62c11b06368 100644 --- a/app/assets/stylesheets/main/layout.scss +++ b/app/assets/stylesheets/base/layout.scss @@ -4,7 +4,7 @@ html { &.touch .tooltip { display: none !important; } body { - padding-top: 47px; + padding-top: 46px; } } diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/base/mixins.scss index e54482d14c3..ccba65e3fd5 100644 --- a/app/assets/stylesheets/main/mixins.scss +++ b/app/assets/stylesheets/base/mixins.scss @@ -121,14 +121,6 @@ } } -@mixin page-title { - color: #333; - line-height: 1.5; - font-weight: normal; - margin-top: 0px; - margin-bottom: 10px; -} - @mixin str-truncated($max_width: 82%) { display: inline-block; overflow: hidden; @@ -137,14 +129,3 @@ white-space: nowrap; max-width: $max_width; } - -@mixin panel-colored { - border: 1px solid #EEE; - background: $box_bg; - @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); - - .panel-heading { - font-weight: bold; - background-color: $box_bg; - } -} diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss new file mode 100644 index 00000000000..54af78ee082 --- /dev/null +++ b/app/assets/stylesheets/base/variables.scss @@ -0,0 +1,37 @@ +$style_color: #474D57; +$hover: #FFF3EB; +$box_bg: #F9F9F9; +$gl-link-color: #446e9b; +$nprogress-color: #c0392b; +$gl-font-size: 14px; +$list-font-size: 15px; +$sidebar_width: 230px; +$avatar_radius: 50%; +$code_font_size: 13px; +$code_line_height: 1.5; + +/* + * State colors: + */ +$gl-success: #019875; +$gl-danger: #d9534f; +$gl-primary: #446e9b; +$gl-info: #029ACF; +$gl-warning: #EB9532; + +$gl-primary: #2C3E50; +$gl-success: #18BC9C; +$gl-info: #3498DB; +$gl-warning: #F39C12; +$gl-danger: #E74C3C; +/* + * Commit Diff Colors + */ +$added: #63c363; +$deleted: #f77; + +/* + * Fonts + */ +$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; +$regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif; diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/generic/avatar.scss index 700cc7e6947..8595887c3b9 100644 --- a/app/assets/stylesheets/generic/avatar.scss +++ b/app/assets/stylesheets/generic/avatar.scss @@ -35,8 +35,8 @@ &.s16 { font-size: 12px; line-height: 1.33; } &.s24 { font-size: 14px; line-height: 1.8; } &.s26 { font-size: 20px; line-height: 1.33; } - &.s32 { font-size: 24px; line-height: 1.33; } - &.s60 { font-size: 45px; line-height: 1.33; } - &.s90 { font-size: 68px; line-height: 1.33; } - &.s160 { font-size: 120px; line-height: 1.33; } + &.s32 { font-size: 22px; line-height: 32px; } + &.s60 { font-size: 32px; line-height: 60px; } + &.s90 { font-size: 36px; line-height: 90px; } + &.s160 { font-size: 96px; line-height: 1.33; } } diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index 3b360275065..0224484d82b 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -1,127 +1,15 @@ .btn { - display: inline-block; - margin-bottom: 0; - font-weight: normal; - text-align: center; - vertical-align: middle; - cursor: pointer; - background-image: none; - border: $btn-border; - white-space: nowrap; - padding: 6px 12px; - font-size: 13px; - line-height: 18px; - border-radius: 4px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; - color: #444444; - background-color: #fff; - text-shadow: none; - - &.hover, - &:hover { - color: #444444; - text-decoration: none; - background-color: #ebebeb; - border-color: #adadad; - } - - &.focus, - &:focus { - color: #444444; - text-decoration: none; - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; - } - - &.active, - &:active { - outline: 0; - background-image: none; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - } - - &.disabled, - &[disabled] { - cursor: not-allowed; - pointer-events: none; - opacity: 0.65; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - } - - &.btn-primary { - color: #ffffff; - background-color: $bg_primary; - border-color: $border_primary; - - &.hover, - &:hover, - &.disabled, - &[disabled] { - color: #ffffff; - } - } - - &.btn-success { - color: #ffffff; - background-color: $bg_success; - border-color: $border_success; - - - &.hover, - &:hover, - &.disabled, - &[disabled] { - color: #ffffff; - } - } - - &.btn-danger { - color: #ffffff; - background-color: $bg_danger; - border-color: $border_danger; - - - &.hover, - &:hover, - &.disabled, - &[disabled] { - color: #ffffff; - } - } - - &.btn-warning { - color: #ffffff; - background-color: $bg_warning; - border-color: $border_warning; - - - &.hover, - &:hover, - &.disabled, - &[disabled] { - color: #ffffff; - } - } + @extend .btn-default; &.btn-new { @extend .btn-success; } &.btn-create { - @extend .wide; @extend .btn-success; } &.btn-save { - @extend .wide; @extend .btn-primary; } @@ -133,11 +21,6 @@ float: right; } - &.wide { - padding-left: 20px; - padding-right: 20px; - } - &.btn-small { padding: 2px 10px; font-size: 12px; @@ -151,16 +34,16 @@ } &.btn-close { - color: $bg_danger; - border-color: $border_danger; + color: $gl-danger; + border-color: $gl-danger; &:hover { color: #B94A48; } } &.btn-reopen { - color: $bg_success; - border-color: $border_success; + color: $gl-success; + border-color: $gl-success; &:hover { color: #468847; } @@ -174,9 +57,12 @@ } } - &.btn-lg { - font-size: 15px; - line-height: 1.4; + &.btn-save { + @extend .btn-primary; + } + + &.btn-new, &.btn-create { + @extend .btn-success; } } diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 3db821fdf76..af8e90eb1a9 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -24,7 +24,7 @@ .slead { color: #666; - font-size: 14px; + font-size: 15px; margin-bottom: 12px; font-weight: normal; line-height: 24px; @@ -61,7 +61,7 @@ pre { .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { - background: $bg_primary; + background: $gl-primary; color: #FFF } @@ -71,7 +71,7 @@ pre { /** FLASH message **/ .author_link { - color: $link_color; + color: $gl-link-color; } .help li { color:$style_color; } @@ -306,20 +306,8 @@ table { width: 100%; } -.broadcast-message { - padding: 10px; - text-align: center; - background: #555; - color: #BBB; -} - -.broadcast-message-preview { - @extend .broadcast-message; - margin-bottom: 20px; -} - .btn-sign-in { - margin-top: 7px; + margin-top: 5px; text-shadow: none; } @@ -337,8 +325,11 @@ table { overflow-x: auto; } -.footer-links a { - margin-right: 15px; +.footer-links { + margin-bottom: 20px; + a { + margin-right: 15px; + } } .search_box { diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss index c8982cdc00d..19bc11086e9 100644 --- a/app/assets/stylesheets/generic/forms.scss +++ b/app/assets/stylesheets/generic/forms.scss @@ -29,7 +29,7 @@ fieldset legend { padding: 17px 20px 18px; margin-top: 18px; margin-bottom: 18px; - background-color: whitesmoke; + background-color: #ecf0f1; border-top: 1px solid #e5e5e5; } diff --git a/app/assets/stylesheets/generic/gfm.scss b/app/assets/stylesheets/generic/gfm.scss index 1427b6a5ae4..8fac5e534fa 100644 --- a/app/assets/stylesheets/generic/gfm.scss +++ b/app/assets/stylesheets/generic/gfm.scss @@ -3,7 +3,7 @@ */ .issue-form, .merge-request-form, .wiki-form { .description { - height: 20em; + height: 16em; border-top-left-radius: 0; } } diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss index 2563ab516e2..9558f241b7c 100644 --- a/app/assets/stylesheets/generic/issue_box.scss +++ b/app/assets/stylesheets/generic/issue_box.scss @@ -11,17 +11,17 @@ margin-right: 5px; &.issue-box-closed { - background-color: $bg_danger; + background-color: $gl-danger; color: #FFF; } &.issue-box-merged { - background-color: $bg_primary; + background-color: $gl-primary; color: #FFF; } &.issue-box-open { - background-color: $bg_success; + background-color: $gl-success; color: #FFF; } diff --git a/app/assets/stylesheets/generic/jquery.scss b/app/assets/stylesheets/generic/jquery.scss index bfbbc7d25e3..871b808bad4 100644 --- a/app/assets/stylesheets/generic/jquery.scss +++ b/app/assets/stylesheets/generic/jquery.scss @@ -41,8 +41,8 @@ } .ui-state-active { - border: 1px solid $bg_primary; - background: $bg_primary; + border: 1px solid $gl-primary; + background: $gl-primary; color: #FFF; } diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss index 5a87cc6c612..eb39b6bb7e9 100644 --- a/app/assets/stylesheets/generic/markdown_area.scss +++ b/app/assets/stylesheets/generic/markdown_area.scss @@ -57,7 +57,6 @@ border: 1px solid #ddd; min-height: 100px; padding: 5px; - font-size: 14px; box-shadow: none; } @@ -77,3 +76,12 @@ } } } + +.markdown-area { + background: #FFF; + border: 1px solid #ddd; + min-height: 100px; + padding: 5px; + box-shadow: none; + width: 100%; +} diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss index 54e06661161..1b0e056216f 100644 --- a/app/assets/stylesheets/generic/mobile.scss +++ b/app/assets/stylesheets/generic/mobile.scss @@ -43,11 +43,34 @@ } } - .page-title .new-issue-link { - display: none; + .page-title { + .note_created_ago, .new-issue-link { + display: none; + } } .issue_edited_ago, .note_edited_ago { display: none; } + + aside { + display: none; + } + + .show-aside { + display: block !important; + } +} + +.show-aside { + display: none; + position: fixed; + right: 0px; + top: 30%; + padding: 5px 15px; + background: #EEE; + font-size: 20px; + color: #777; + z-index: 100; + @include box-shadow(0 1px 2px #DDD); } diff --git a/app/assets/stylesheets/sections/nav_sidebar.scss b/app/assets/stylesheets/generic/nav_sidebar.scss index b35043821da..c14f12284da 100644 --- a/app/assets/stylesheets/sections/nav_sidebar.scss +++ b/app/assets/stylesheets/generic/nav_sidebar.scss @@ -12,7 +12,6 @@ .sidebar-wrapper { z-index: 99; - overflow-y: auto; background: #F5F5F5; } @@ -40,12 +39,16 @@ .nav-sidebar li { &.active a { - color: #111; - background: #EEE; + color: #333; + background: #FFF !important; font-weight: bold; + border: 1px solid #EEE; + border-right: 1px solid transparent; + border-left: 3px solid $style_color; &.no-highlight { - background: none; + background: none !important; + border: none; } i { @@ -65,7 +68,7 @@ color: #555; display: block; text-decoration: none; - padding: 6px 15px; + padding: 8px 15px; font-size: 13px; line-height: 20px; text-shadow: 0 1px 2px #FFF; @@ -74,7 +77,7 @@ &:hover { text-decoration: none; color: #333; - background: #DDD; + background: #EEE; } &:active, &:focus { @@ -105,7 +108,7 @@ width: $sidebar_width; .nav-sidebar { - margin-top: 20px; + margin-top: 29px; position: fixed; top: 45px; width: $sidebar_width; @@ -122,33 +125,49 @@ .sidebar-wrapper { width: 52px; - overflow-x: hidden; .nav-sidebar { - margin-top: 20px; - position: absolute; + margin-top: 29px; + position: fixed; top: 45px; width: 52px; li a { padding-left: 18px; font-size: 14px; - padding: 10px 15px; + padding: 8px 15px; text-align: center; + & > span { display: none; } } } + + .collapse-nav a { + left: 0px; + padding: 7px 23px 3px 22px; + } } } .collapse-nav a { position: fixed; - bottom: 15px; - padding: 10px; - background: #DDD; + top: 46px; + padding: 5px 13px 5px 13px; + left: 197px; + font-size: 13px; + background: #EEE; + color: black; + border-left: 1px solid rgba(0,0,0,0.035); + border-right: 1px solid rgba(0,0,0,0.035); +} + +.collapse-nav a:hover { + text-decoration: none; + color: #333; + background: #eaeaea; } @media (max-width: $screen-md-max) { diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss index d85e80a512b..af0ecb192d6 100644 --- a/app/assets/stylesheets/generic/selects.scss +++ b/app/assets/stylesheets/generic/selects.scss @@ -3,9 +3,8 @@ .select2-choice { background: #FFF; border-color: #BBB; - padding: 6px 12px; - font-size: 13px; - line-height: 18px; + padding: 6px 14px; + line-height: 1.42857143; height: auto; .select2-arrow { @@ -20,7 +19,7 @@ } .select2-container-multi .select2-choices .select2-search-field input { - padding: 6px 12px; + padding: 8px 14px; font-size: 13px; line-height: 18px; height: auto; @@ -42,7 +41,7 @@ .select2-results { max-height: 350px; .select2-highlighted { - background: $bg_primary; + background: $gl-primary; } } } diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss deleted file mode 100644 index f6311ef74e8..00000000000 --- a/app/assets/stylesheets/generic/sidebar.scss +++ /dev/null @@ -1,46 +0,0 @@ -.ui.sidebar { - z-index: 1000 !important; - background: #fff; - padding: 10px; - width: 285px; -} - -.ui.right.sidebar { - border-left: 1px solid #e1e1e1; - border-right: 0; -} - -.sidebar-expand-button { - cursor: pointer; - transition: all 0.4s; - -moz-transition: all 0.4s; - -webkit-transition: all 0.4s; -} - -.fixed.sidebar-expand-button { - background: #f9f9f9; - color: #555; - padding: 9px 12px 6px 14px; - border: 1px solid #E1E1E1; - border-right: 0; - position: fixed; - top: 108px; - right: 0px; - margin-right: 0; - &:hover { - background: #ddd; - color: #333; - padding-right: 25px; - } -} - -.btn.btn-default.sidebar-expand-button { - margin-left: 12px; - display: inline-block !important; -} - -@media (min-width: 767px) { -.btn.btn-default.sidebar-expand-button { - display: none!important; - } -} diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index 58243bc5ba2..4d940ee6b29 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -2,24 +2,12 @@ * Headers * */ -h1.page-title { - @include page-title; - font-size: 28px; -} - -h2.page-title { - @include page-title; - font-size: 24px; -} - -h3.page-title { - @include page-title; - font-size: 22px; -} - -h6 { - color: #888; - text-transform: uppercase; +.page-title { + margin-top: 0px; + color: #333; + line-height: 1.5; + font-weight: normal; + margin-bottom: 5px; } /** CODE **/ @@ -32,52 +20,6 @@ pre { } } -/** - * Links - * - */ -a { - outline: none; - color: $link_color; - &:hover { - text-decoration: underline; - color: $link_hover_color; - } - - &:focus { - text-decoration: underline; - } - - &.darken { - color: $style_color; - } - - &.lined { - text-decoration: underline; - &:hover { text-decoration: underline; } - } - - &.gray { - color: gray; - } - - &.supp_diff_link { - text-align: center; - padding: 20px 0; - background: #f1f1f1; - width: 100%; - float: left; - } - - &.neib { - margin-right: 15px; - } -} - -a:focus { - outline: none; -} - .monospace { font-family: $monospace_font; } @@ -131,4 +73,4 @@ textarea.js-gfm-input { .strikethrough { text-decoration: line-through; -}
\ No newline at end of file +} diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index 4095d35b05f..c8cb18ec35f 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -1,6 +1,10 @@ /* https://github.com/MozMorris/tomorrow-pygments */ +pre.code.highlight.dark, .code.dark { + background-color: #1d1f21; + color: #c5c8c6; + pre.code, .line-numbers, .line-numbers a { @@ -12,8 +16,9 @@ border-left: 1px solid #666; } - pre.hll { - background-color: #fff !important; + // highlight line via anchor + pre .hll { + background-color: #557 !important; } .hll { background-color: #373b41 } diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 730018e3e28..001e8b31020 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -1,6 +1,10 @@ /* https://github.com/richleland/pygments-css/blob/master/monokai.css */ +pre.code.monokai, .code.monokai { + background: #272822; + color: #f8f8f2; + pre.highlight, .line-numbers, .line-numbers a { @@ -12,6 +16,11 @@ border-left: 1px solid #555; } + // highlight line via anchor + pre .hll { + background-color: #49483e !important; + } + .hll { background-color: #49483e } .c { color: #75715e } /* Comment */ .err { color: #960050; background-color: #1e0010 } /* Error */ diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index be6904100ec..f5b827e7c02 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -1,6 +1,10 @@ /* https://gist.github.com/qguv/7936275 */ +pre.code.highlight.solarized-dark, .code.solarized-dark { + background-color: #002b36; + color: #93a1a1; + pre.code, .line-numbers, .line-numbers a { @@ -12,6 +16,11 @@ border-left: 1px solid #113b46; } + // highlight line via anchor + pre .hll { + background-color: #174652 !important; + } + /* Solarized Dark For use with Jekyll and Pygments diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index 55be6e30383..6b44c00c305 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -1,6 +1,10 @@ /* https://gist.github.com/qguv/7936275 */ +pre.code.highlight.solarized-light, .code.solarized-light { + background-color: #fdf6e3; + color: #586e75; + pre.code, .line-numbers, .line-numbers a { @@ -12,6 +16,11 @@ border-left: 1px solid #c5d0d4; } + // highlight line via anchor + pre .hll { + background-color: #ddd8c5 !important; + } + /* Solarized Light For use with Jekyll and Pygments diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 050a5d241a6..a52ffc971d1 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -1,6 +1,10 @@ /* https://github.com/aahan/pygments-github-style */ +pre.code.highlight.white, .code.white { + background-color: #fff; + color: #333; + pre.highlight, .line-numbers, .line-numbers a { @@ -12,6 +16,11 @@ border-left: 1px solid #bbb; } + // highlight line via anchor + pre .hll { + background-color: #f8eec7 !important; + } + .hll { background-color: #f8f8f8 } .c { color: #999988; font-style: italic; } .err { color: #a61717; background-color: #e3d2d2; } diff --git a/app/assets/stylesheets/main/fonts.scss b/app/assets/stylesheets/main/fonts.scss deleted file mode 100644 index f945aaca848..00000000000 --- a/app/assets/stylesheets/main/fonts.scss +++ /dev/null @@ -1,3 +0,0 @@ -/** Typo **/ -$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; -$regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif; diff --git a/app/assets/stylesheets/main/variables.scss b/app/assets/stylesheets/main/variables.scss deleted file mode 100644 index acbf5be94a3..00000000000 --- a/app/assets/stylesheets/main/variables.scss +++ /dev/null @@ -1,63 +0,0 @@ -/* - * General Colors - */ -$style_color: #474D57; -$hover: #FFF3EB; -$box_bg: #F9F9F9; - -/* - * Link colors - */ -$link_color: #446e9b; -$link_hover_color: darken($link-color, 10%); - -$btn-border: 1px solid #ccc; - -/* - * Success colors (green) - */ -$border_success: #019875; -$bg_success: #019875; - -/* - * Danger colors (red) - */ -$border_danger: #d43f3a; -$bg_danger: #d9534f; - -/* - * Primary colors (blue) - */ -$border_primary: #446e9b; -$bg_primary: #446e9b; - -/* - * Warning colors (yellow) - */ -$bg_warning: #EB9532; -$border_warning: #EB9532; - -/** - * Commit Diff Colors - */ -$added: #63c363; -$deleted: #f77; - -/** - * NProgress customize - */ -$nprogress-color: #c0392b; - -/** - * Font sizes - */ -$list-font-size: 15px; - -/** - * Sidebar navigation width - */ -$sidebar_width: 230px; - -$avatar_radius: 50%; -$code_font_size: 13px; -$code_line_height: 1.5; diff --git a/app/assets/stylesheets/sections/admin.scss b/app/assets/stylesheets/pages/admin.scss index a51deee7970..144852e7874 100644 --- a/app/assets/stylesheets/sections/admin.scss +++ b/app/assets/stylesheets/pages/admin.scss @@ -50,3 +50,14 @@ line-height: 2; } } + +.broadcast-message { + @extend .alert-warning; + padding: 10px; + text-align: center; +} + +.broadcast-message-preview { + @extend .broadcast-message; + margin-bottom: 20px; +} diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss new file mode 100644 index 00000000000..f46d6542c03 --- /dev/null +++ b/app/assets/stylesheets/pages/commit.scss @@ -0,0 +1,122 @@ +.commit-title{ + display: block; +} + +.commit-title{ + margin-bottom: 10px; +} + +.commit-author, .commit-committer{ + display: block; + color: #999; + font-weight: normal; + font-style: italic; +} + +.commit-author strong, .commit-committer strong{ + font-weight: bold; + font-style: normal; +} + +.commit-description { + background: none; + border: none; + margin: 0; + padding: 0; + margin-top: 10px; +} + +.commit-stat-summary { + color: #666; + font-size: 14px; + font-weight: normal; + padding: 10px 0; +} + +.commit-info-row { + margin-bottom: 10px; + .avatar { + @extend .avatar-inline; + } + .commit-committer-link, + .commit-author-link { + color: #444; + font-weight: bold; + } +} + +.commit-box { + margin: 10px 0; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + padding: 20px 0; + + .commit-title { + margin: 0; + } + + .commit-description { + margin-top: 15px; + } +} + +.file-stats a { + color: $style_color; +} + +.file-stats { + .new-file { + a { + color: #090; + } + i { + color: #1BCF00; + } + } + .renamed-file { + i { + color: #FE9300; + } + } + .deleted-file { + a { + color: #B00; + } + i { + color: #EE0000; + } + } + .edit-file{ + i{ + color: #555; + } + } +} + +/* + * Commit message textarea for web editor and + * custom merge request message + */ +.commit-message-container { + background-color: $body-bg; + position: relative; + font-family: $monospace_font; + $left: 12px; + .max-width-marker { + width: 72ch; + color: rgba(0, 0, 0, 0.0); + font-family: inherit; + left: $left; + height: 100%; + border-right: 1px solid mix($input-border, white); + position: absolute; + z-index: 1; + } + > textarea { + background-color: rgba(0, 0, 0, 0.0); + font-family: inherit; + padding-left: $left; + position: relative; + z-index: 2; + } +} diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss new file mode 100644 index 00000000000..e167d044e47 --- /dev/null +++ b/app/assets/stylesheets/pages/commits.scss @@ -0,0 +1,124 @@ +.commits-compare-switch{ + background: image-url("switch_icon.png") no-repeat center center; + width: 32px; + height: 32px; + text-indent: -9999px; + float: left; + margin-right: 9px; + border: 1px solid #DDD; + @include border-radius(4px); + padding: 4px; + background-color: #EEE; +} + + +.lists-separator { + margin: 10px 0; + border-color: #DDD; +} + +.commits-row { + ul { + margin: 0; + + li.commit { + padding: 8px 0; + } + } + + .commits-row-date { + font-size: 15px; + line-height: 20px; + margin-bottom: 5px; + } +} + +.commits-feed-holder { + float: right; + + .btn { + padding: 4px 12px; + } +} + +li.commit { + .commit-row-title { + font-size: $list-font-size; + line-height: 20px; + margin-bottom: 2px; + + .notes_count { + float: right; + margin-right: 10px; + } + + .commit_short_id { + min-width: 65px; + font-family: $monospace_font; + } + + .str-truncated { + max-width: 70%; + } + + .commit-row-message { + color: #444; + + &:hover { + text-decoration: underline; + } + } + + .text-expander { + background: #eee; + color: #555; + padding: 0 5px; + cursor: pointer; + margin-left: 4px; + &:hover { + background-color: #ddd; + } + } + } + + .commit-row-description { + font-size: 14px; + border-left: 1px solid #EEE; + padding: 10px 15px; + margin: 5px 0 10px 5px; + background: #f9f9f9; + display: none; + + pre { + border: none; + background: inherit; + padding: 0; + margin: 0; + } + } + + .commit-row-info { + color: #777; + line-height: 24px; + font-size: 13px; + + a { + color: #777; + } + + .committed_ago { + display: inline-block; + } + } + + &.inline-commit { + .commit-row-title { + font-size: 13px; + } + + .committed_ago { + float: right; + @extend .cgray; + } + } +} diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss index 77d403cc687..5a543a852c2 100644 --- a/app/assets/stylesheets/sections/dashboard.scss +++ b/app/assets/stylesheets/pages/dashboard.scss @@ -23,28 +23,6 @@ } } -.dash-sidebar-tabs { - margin-bottom: 2px; - border: none; - margin: 0 !important; - - li { - &.active { - a { - background-color: #EEE; - border-bottom: 1px solid #EEE !important; - &:hover { - background: #eee; - } - } - } - - a { - border-color: #DDD !important; - } - } -} - .project-row, .group-row { padding: 0 !important; font-size: 14px; @@ -84,7 +62,6 @@ margin-left: 10px; float: left; margin-right: 15px; - font-size: 20px; margin-bottom: 15px; i { @@ -112,8 +89,8 @@ } .dash-new-project { - background: $bg_success; - border: 1px solid $border_success; + background: $gl-success; + border: 1px solid $gl-success; a { color: #FFF; diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/pages/diff.scss index f47ea329827..54311a68852 100644 --- a/app/assets/stylesheets/sections/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -8,6 +8,7 @@ border-bottom: 1px solid #CCC; padding: 5px 5px 5px 10px; color: #555; + z-index: 10; > span { font-family: $monospace_font; diff --git a/app/assets/stylesheets/sections/editor.scss b/app/assets/stylesheets/pages/editor.scss index 88aa256e56e..88aa256e56e 100644 --- a/app/assets/stylesheets/sections/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss diff --git a/app/assets/stylesheets/sections/errors.scss b/app/assets/stylesheets/pages/errors.scss index 32d2d7b1dbf..32d2d7b1dbf 100644 --- a/app/assets/stylesheets/sections/errors.scss +++ b/app/assets/stylesheets/pages/errors.scss diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/pages/events.scss index 9582c995980..3e9e36e477e 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -46,7 +46,6 @@ border-bottom: 1px solid #eee; .event-title { @include str-truncated(72%); - color: #333; font-weight: 500; font-size: 14px; .author_name { @@ -54,6 +53,7 @@ } } .event-body { + font-size: 13px; margin-left: 35px; margin-right: 80px; color: #777; @@ -64,6 +64,10 @@ .md { font-size: 13px; + + iframe.twitter-share-button { + vertical-align: bottom; + } } pre { @@ -180,6 +184,11 @@ } } -.event_filter li a { - padding: 5px 10px; +.event_filter { + li a { + font-size: 13px; + padding: 5px 10px; + background: rgba(0,0,0,0.045); + margin-left: 4px; + } } diff --git a/app/assets/stylesheets/sections/explore.scss b/app/assets/stylesheets/pages/explore.scss index 9b92128624c..9b92128624c 100644 --- a/app/assets/stylesheets/sections/explore.scss +++ b/app/assets/stylesheets/pages/explore.scss diff --git a/app/assets/stylesheets/sections/graph.scss b/app/assets/stylesheets/pages/graph.scss index 3d878d1e528..3d878d1e528 100644 --- a/app/assets/stylesheets/sections/graph.scss +++ b/app/assets/stylesheets/pages/graph.scss diff --git a/app/assets/stylesheets/sections/groups.scss b/app/assets/stylesheets/pages/groups.scss index e49fe1a9dd6..e49fe1a9dd6 100644 --- a/app/assets/stylesheets/sections/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/pages/header.scss index e255cbcada8..26b4d04106e 100644 --- a/app/assets/stylesheets/sections/header.scss +++ b/app/assets/stylesheets/pages/header.scss @@ -86,7 +86,7 @@ header { .container { width: 100% !important; - padding-left: 0px; + padding: 0px; } /** @@ -100,18 +100,14 @@ header { a { float: left; - padding: 0px; - margin: 0 6px; - - h1 { - margin: 0; - background: image-url('logo-black.png') no-repeat center center; - background-size: 32px; - float: left; - height: 46px; - width: 40px; - @include header-font; - text-indent: -9999px; + padding: 5px 0; + height: 46px; + width: 52px; + text-align: center; + + img { + width: 36px; + height: 36px; } } &:hover { @@ -134,14 +130,13 @@ header { } .profile-pic { - position: relative; - top: -1px; - padding-right: 0px !important; + padding: 0px !important; + width: 46px; + height: 46px; + margin-left: 5px; img { - width: 50px; - height: 50px; - margin: -15px; - margin-left: 5px; + width: 46px; + height: 46px; } } @@ -174,68 +169,6 @@ header { @include transition(all 0.15s ease-in 0s); } } - - - /* - * Dark header - * - */ - &.header-dark { - &.navbar-gitlab { - .navbar-inner { - background: #708090; - border-bottom: 1px solid #AAA; - - .navbar-toggle { color: #fff; } - - .nav > li > a { - color: #AAA; - - &:hover, &:focus, &:active { - background: none; - color: #FFF; - } - } - } - } - - .turbolink-spinner { - color: #FFF; - } - - .search { - .search-input { - background-color: #D2D5DA; - background-color: rgba(255, 255, 255, 0.5); - border: 1px solid #AAA; - - &:focus { - background-color: white; - } - } - } - .search-input::-webkit-input-placeholder { - color: #666; - } - .app_logo { - a { - h1 { - background: image-url('logo-white.png') no-repeat center center; - background-size: 32px; - color: #fff; - } - } - } - .title { - a { - color: #FFF; - &:hover { - text-decoration: underline; - } - } - color: #fff; - } - } } .search .search-input { diff --git a/app/assets/stylesheets/sections/help.scss b/app/assets/stylesheets/pages/help.scss index 07c62f98c36..6da7a2511a2 100644 --- a/app/assets/stylesheets/sections/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -12,7 +12,6 @@ color: #888; a { - font-size: 14px; margin-right: 3px; } } @@ -29,7 +28,6 @@ th { padding-top: 15px; - font-size: 14px; line-height: 1.5; color: #333; text-align: left diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss new file mode 100644 index 00000000000..3df4bb84bd2 --- /dev/null +++ b/app/assets/stylesheets/pages/import.scss @@ -0,0 +1,18 @@ +i.icon-gitorious { + display: inline-block; + background-position: 0px 0px; + background-size: contain; + background-repeat: no-repeat; +} + +i.icon-gitorious-small { + background-image: image-url('gitorious-logo-blue.png'); + width: 13px; + height: 13px; +} + +i.icon-gitorious-big { + background-image: image-url('gitorious-logo-black.png'); + width: 18px; + height: 18px; +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss new file mode 100644 index 00000000000..d8d12338859 --- /dev/null +++ b/app/assets/stylesheets/pages/issuable.scss @@ -0,0 +1,41 @@ +@media (max-width: $screen-sm-max) { + .issuable-affix { + margin-top: 20px; + } +} + +@media (max-width: $screen-md-max) { + .issuable-affix { + position: static; + } +} + +@media (min-width: $screen-md-max) { + .issuable-affix { + &.affix-top { + position: static; + } + + &.affix { + position: fixed; + top: 70px; + width: 220px; + } + } +} + +.issuable-context-title { + font-size: 15px; + line-height: 1.4; + margin-bottom: 5px; + + .avatar { + margin-left: 0; + } + + label { + color: #666; + font-weight: normal; + margin-right: 4px; + } +} diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/pages/issues.scss index 7a9d3334d96..4ea34cc1dac 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -6,10 +6,12 @@ .issue-title { margin-bottom: 5px; font-size: $list-font-size; + font-weight: bold; } .issue-info { color: #999; + font-size: 13px; } .issue-check { @@ -39,7 +41,7 @@ } .check-all-holder { - height: 32px; + height: 36px; float: left; margin-right: 12px; padding: 6px 15px; @@ -94,8 +96,15 @@ } } -.issue-show-labels .color-label { - padding: 6px 10px; +.issue-show-labels { + a { + margin-right: 5px; + margin-bottom: 5px; + display: inline-block; + .color-label { + padding: 6px 10px; + } + } } form.edit-issue { @@ -163,9 +172,9 @@ form.edit-issue { } } -h3.issue-title { +h2.issue-title { margin-top: 0; - font-size: 2em; + font-weight: bold; } .context .select2-container { diff --git a/app/assets/stylesheets/sections/labels.scss b/app/assets/stylesheets/pages/labels.scss index d1590e42fcb..d1590e42fcb 100644 --- a/app/assets/stylesheets/sections/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/pages/login.scss index 3a3644c12b7..d366300511e 100644 --- a/app/assets/stylesheets/sections/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -40,8 +40,7 @@ .login-heading h3 { font-weight: 300; line-height: 1.5; - margin: 0; - display: none; + margin: 0 0 10px 0; } .login-footer { diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 0e27c389387..9bd34b7376f 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -24,6 +24,7 @@ .accept-control { display: inline-block; + margin: 0; margin-left: 20px; padding: 10px 0; line-height: 20px; @@ -31,6 +32,7 @@ .remove_source_checkbox { margin: 0; + font-weight: bold; } } } @@ -89,10 +91,12 @@ .merge-request-title { margin-bottom: 5px; font-size: $list-font-size; + font-weight: bold; } .merge-request-info { color: #999; + font-size: 13px; .merge-request-labels { display: inline-block; @@ -133,8 +137,8 @@ background-color: #F5F5F5; &.ci-success { - color: $bg_success; - border-color: $border_success; + color: $gl-success; + border-color: $gl-success; background-color: #F1FAF1; } @@ -145,20 +149,20 @@ } &.ci-running { - color: $bg_warning; - border-color: $border_warning; + color: $gl-warning; + border-color: $gl-warning; background-color: #FAF5F1; } &.ci-failed { - color: $bg_danger; - border-color: $border_danger; + color: $gl-danger; + border-color: $gl-danger; background-color: #FAF1F1; } &.ci-error { - color: $bg_danger; - border-color: $border_danger; + color: $gl-danger; + border-color: $gl-danger; background-color: #FAF1F1; } } @@ -185,6 +189,13 @@ } } -.merge-request-show-labels .label { - padding: 6px 10px; +.merge-request-show-labels { + a { + margin-right: 5px; + margin-bottom: 5px; + display: inline-block; + .color-label { + padding: 6px 10px; + } + } } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss new file mode 100644 index 00000000000..15e3948e402 --- /dev/null +++ b/app/assets/stylesheets/pages/milestone.scss @@ -0,0 +1,9 @@ +.issues-sortable-list .str-truncated { + max-width: 90%; +} + +li.milestone { + h4 { + font-weight: bold; + } +} diff --git a/app/assets/stylesheets/sections/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 61a877a5e43..a0522030785 100644 --- a/app/assets/stylesheets/sections/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -169,7 +169,7 @@ color: #999; background: #FFF; padding: 5px; - margin-top: -7px; + margin-top: -11px; border: 1px solid #DDD; font-size: 13px; } diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/pages/notes.scss index 5494845eb8c..384ff6d740c 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -38,13 +38,11 @@ ul.notes { .author { color: #333; font-weight: bold; - font-size: 14px; &:hover { - color: $link_color; + color: $gl-link-color; } } .author-username { - font-size: 14px; } } @@ -57,15 +55,28 @@ ul.notes { .note { display: block; position:relative; - .attachment { - font-size: 14px; - } .note-body { overflow: auto; .note-text { overflow: auto; word-wrap: break-word; @include md-typography; + + a[href*="/uploads/"] { + &:before { + margin-right: 4px; + + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + content: "\f0c6"; + } + + &:hover:before { + text-decoration: none; + } + } } } .note-header { @@ -137,7 +148,6 @@ ul.notes { @extend .cgray; &:hover { - color: $link_hover_color; &.danger { @extend .cred; } } } @@ -165,10 +175,11 @@ ul.notes { background: #FFF; padding: 4px; font-size: 16px; - color: $link_color; + color: $gl-link-color; margin-left: -60px; position: absolute; z-index: 10; + width: 32px; transition: all 0.2s ease; @@ -177,8 +188,9 @@ ul.notes { filter: alpha(opacity=0); &:hover { - font-size: 24px; - background: $bg_primary; + width: 38px; + font-size: 20px; + background: $gl-info; color: #FFF; @include show-add-diff-note; } diff --git a/app/assets/stylesheets/sections/notifications.scss b/app/assets/stylesheets/pages/notifications.scss index f11c5dff4ab..cc273f55222 100644 --- a/app/assets/stylesheets/sections/notifications.scss +++ b/app/assets/stylesheets/pages/notifications.scss @@ -10,13 +10,13 @@ } .ns-part { - color: $bg_primary; + color: $gl-primary; } .ns-watch { - color: $bg_success; + color: $gl-success; } .ns-mute { - color: $bg_danger; + color: $gl-danger; } diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/pages/profile.scss index 0ab62b7ae49..81afe05162f 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -80,6 +80,10 @@ &.violet { background: #548; } + + &.blue { + background: #2980b9; + } } } } diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/pages/projects.scss index 3bb3779c294..bfd05973d75 100644 --- a/app/assets/stylesheets/sections/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -99,23 +99,6 @@ margin-right: 45px; } - .btn, - .form-control { - border: 1px solid #E1E1E1; - box-shadow: none; - padding: 6px 9px; - } - - .btn { - background: none; - color: $link_color; - - &.active { - color: #333; - font-weight: bold; - } - } - .form-control { cursor: auto; @extend .monospace; @@ -265,15 +248,15 @@ ul.nav.nav-projects-tabs { } .vs-public { - color: $bg_primary; + color: $gl-primary; } .vs-internal { - color: $bg_warning; + color: $gl-warning; } .vs-private { - color: $bg_success; + color: $gl-success; } .breadcrumb.repo-breadcrumb { diff --git a/app/assets/stylesheets/sections/search.scss b/app/assets/stylesheets/pages/search.scss index bdaa17ac339..bdaa17ac339 100644 --- a/app/assets/stylesheets/sections/search.scss +++ b/app/assets/stylesheets/pages/search.scss diff --git a/app/assets/stylesheets/sections/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index d79591d9915..d79591d9915 100644 --- a/app/assets/stylesheets/sections/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss diff --git a/app/assets/stylesheets/sections/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss index b9be47e7700..b9be47e7700 100644 --- a/app/assets/stylesheets/sections/stat_graph.scss +++ b/app/assets/stylesheets/pages/stat_graph.scss diff --git a/app/assets/stylesheets/sections/themes.scss b/app/assets/stylesheets/pages/themes.scss index e69de29bb2d..e69de29bb2d 100644 --- a/app/assets/stylesheets/sections/themes.scss +++ b/app/assets/stylesheets/pages/themes.scss diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/pages/tree.scss index ff9464e217f..ce02cdb1652 100644 --- a/app/assets/stylesheets/sections/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -39,14 +39,9 @@ .tree-item-file-name { max-width: 320px; vertical-align: middle; - a { - &:hover { - color: $link_hover_color; - } - } - i { - color: $bg_primary; + i, a { + color: $gl-link-color; } img { @@ -66,13 +61,18 @@ .tree_author { padding-right: 8px; + + .commit-author-name { + color: gray; + } } .tree_commit { color: gray; .tree-commit-link { - color: #444; + color: gray; + &:hover { text-decoration: underline; } @@ -120,13 +120,13 @@ } .readme-holder { - border-top: 1px dashed #CCC; - padding-top: 10px; - .readme-file-title { font-size: 14px; + font-weight: bold; margin-bottom: 20px; color: #777; + border-bottom: 1px solid #DDD; + padding: 10px 0; } } diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss new file mode 100644 index 00000000000..277afa1db9e --- /dev/null +++ b/app/assets/stylesheets/pages/ui_dev_kit.scss @@ -0,0 +1,9 @@ +.gitlab-ui-dev-kit { + > h2 { + font-size: 27px; + border-bottom: 1px solid #CCC; + color: #666; + margin: 30px 0; + font-weight: bold; + } +} diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/pages/votes.scss index ba0a519dca6..ba0a519dca6 100644 --- a/app/assets/stylesheets/sections/votes.scss +++ b/app/assets/stylesheets/pages/votes.scss diff --git a/app/assets/stylesheets/sections/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index dfaeba41cf6..dfaeba41cf6 100644 --- a/app/assets/stylesheets/sections/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss deleted file mode 100644 index 2e274d06c12..00000000000 --- a/app/assets/stylesheets/sections/commits.scss +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Commit file - */ -.commit-committer-link, -.commit-author-link { - font-size: 13px; - color: #555; - &:hover { - color: #999; - } -} - -/** COMMIT BLOCK **/ -.commit-title{ - display: block; -} -.commit-title{ - margin-bottom: 10px; -} -.commit-author, .commit-committer{ - display: block; - color: #999; - font-weight: normal; - font-style: italic; -} -.commit-author strong, .commit-committer strong{ - font-weight: bold; - font-style: normal; -} - - -.file-stats a { - color: $style_color; -} - -.file-stats { - .new-file { - a { - color: #090; - } - i { - color: #1BCF00; - } - } - .renamed-file { - i { - color: #FE9300; - } - } - .deleted-file { - a { - color: #B00; - } - i { - color: #EE0000; - } - } - .edit-file{ - i{ - color: #555; - } - } -} - -.label_commit { - @include border-radius(4px); - padding: 2px 4px; - font-size: 13px; - background: #474D57; - color: #fff; - font-family: $monospace_font; -} - - -.commits-compare-switch{ - background: image-url("switch_icon.png") no-repeat center center; - width: 32px; - height: 32px; - text-indent: -9999px; - float: left; - margin-right: 9px; - border: 1px solid #DDD; - @include border-radius(4px); - padding: 4px; - background-color: #EEE; -} - -.commit-description { - background: none; - border: none; - margin: 0; - padding: 0; - margin-top: 10px; -} - -.commit-box { - margin: 10px 0; - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - padding: 20px 0; - - .commit-title { - margin: 0; - } - - .commit-description { - margin-top: 15px; - } -} - - -.commit-stat-summary { - color: #666; - font-size: 14px; - font-weight: normal; - padding: 10px 0; -} - -.commit-info-row { - margin-bottom: 10px; - .avatar { - @extend .avatar-inline; - } - .commit-committer-link, - .commit-author-link { - color: #444; - font-weight: bold; - } -} - -.lists-separator { - margin: 10px 0; - border-top: 1px dashed #CCC; -} - -/** - * COMMIT ROW - */ -li.commit { - .commit-row-title { - font-size: $list-font-size; - margin-bottom: 2px; - - .notes_count { - float: right; - margin-right: 10px; - } - - .commit_short_id { - min-width: 65px; - font-family: $monospace_font; - } - - .str-truncated { - max-width: 70%; - } - - .commit-row-message { - color: #333; - &:hover { - color: #444; - text-decoration: underline; - } - } - - .text-expander { - background: #eee; - color: #555; - padding: 0 5px; - cursor: pointer; - margin-left: 4px; - &:hover { - background-color: #ddd; - } - } - } - - .commit-row-description { - font-size: 14px; - border-left: 1px solid #EEE; - padding: 10px 15px; - margin: 5px 0 10px 5px; - background: #f9f9f9; - display: none; - - pre { - border: none; - background: inherit; - padding: 0; - margin: 0; - } - } - - .commit-row-info { - color: #777; - - a { - color: #777; - } - - .committed_ago { - float: right; - } - } - - &.inline-commit { - .commit-row-title { - font-size: 13px; - } - - .committed_ago { - float: right; - @extend .cgray; - } - } -} - -.commits-feed-holder { - float: right; - .btn { - padding: 4px 12px; - } -} - -.commit-message-container { - background-color: $body-bg; - position: relative; - font-family: $monospace_font; - $left: 12px; - .max-width-marker { - width: 72ch; - color: rgba(0, 0, 0, 0.0); - font-family: inherit; - left: $left; - height: 100%; - border-right: 1px solid mix($input-border, white); - position: absolute; - z-index: 1; - } - > textarea { - background-color: rgba(0, 0, 0, 0.0); - font-family: inherit; - padding-left: $left; - position: relative; - z-index: 2; - } -} diff --git a/app/assets/stylesheets/sections/markdown_area.scss b/app/assets/stylesheets/sections/markdown_area.scss deleted file mode 100644 index 8ee8eaa4ee7..00000000000 --- a/app/assets/stylesheets/sections/markdown_area.scss +++ /dev/null @@ -1,9 +0,0 @@ -.markdown-area { - background: #FFF; - border: 1px solid #ddd; - min-height: 100px; - padding: 5px; - font-size: 14px; - box-shadow: none; - width: 100%; -} diff --git a/app/assets/stylesheets/sections/milestone.scss b/app/assets/stylesheets/sections/milestone.scss deleted file mode 100644 index d20391e38fd..00000000000 --- a/app/assets/stylesheets/sections/milestone.scss +++ /dev/null @@ -1,3 +0,0 @@ -.issues-sortable-list .str-truncated { - max-width: 70%; -} diff --git a/app/assets/stylesheets/themes/dark-theme.scss b/app/assets/stylesheets/themes/dark-theme.scss new file mode 100644 index 00000000000..b7b22a8724e --- /dev/null +++ b/app/assets/stylesheets/themes/dark-theme.scss @@ -0,0 +1,63 @@ +@mixin dark-theme($color-light, $color, $color-darker, $color-dark) { + header { + &.navbar-gitlab { + .navbar-inner { + background: $color; + + .navbar-toggle { + color: #FFF; + } + + .app_logo, .navbar-toggle { + &:hover { + background-color: $color-darker; + } + } + + .app_logo { + background-color: $color-dark; + } + + .title { + color: #FFF; + + a { + color: #FFF; + &:hover { + text-decoration: underline; + } + } + } + + .search { + .search-input { + background-color: $color-light; + background-color: rgba(255, 255, 255, 0.5); + border: 1px solid $color-light; + + &:focus { + background-color: white; + } + } + } + + .search-input::-webkit-input-placeholder { + color: #666; + } + + .nav > li > a { + color: $color-light; + + &:hover, &:focus, &:active { + background: none; + color: #FFF; + } + } + + .search-input { + border-color: $color-light; + } + } + } + } +} diff --git a/app/assets/stylesheets/themes/ui_basic.scss b/app/assets/stylesheets/themes/ui_basic.scss index 0dad9917b55..097d5c5b73c 100644 --- a/app/assets/stylesheets/themes/ui_basic.scss +++ b/app/assets/stylesheets/themes/ui_basic.scss @@ -10,8 +10,15 @@ background: #F1F1F1; border-bottom: 1px solid #DDD; - .app_logo { - background-color: #DDD; + .title { + color: #555; + + a { + color: #555; + &:hover { + text-decoration: underline; + } + } } .nav > li > a { diff --git a/app/assets/stylesheets/themes/ui_blue.scss b/app/assets/stylesheets/themes/ui_blue.scss new file mode 100644 index 00000000000..cb7980b5a07 --- /dev/null +++ b/app/assets/stylesheets/themes/ui_blue.scss @@ -0,0 +1,6 @@ +/** + * Modern GitLab UI theme + */ +.ui_blue { + @include dark-theme(#BECDE9, #2980b9, #1970a9, #096099); +} diff --git a/app/assets/stylesheets/themes/ui_color.scss b/app/assets/stylesheets/themes/ui_color.scss index 3c441a8e098..7ac6903b2e4 100644 --- a/app/assets/stylesheets/themes/ui_color.scss +++ b/app/assets/stylesheets/themes/ui_color.scss @@ -1,42 +1,6 @@ /** - * This file represent some UI that can be changed - * during web app restyle or theme select. - * - * Next items should be placed there - * - link colors - * - header restyles - * + * Violet GitLab UI theme */ .ui_color { - /* - * Application Header - * - */ - header { - @extend .header-dark; - &.navbar-gitlab { - .navbar-inner { - background: #548; - border-bottom: 1px solid #436; - .app_logo, .navbar-toggle { - &:hover { - background-color: #436; - } - } - .app_logo { - background-color: #325; - } - .nav > li > a { - color: #98C; - } - .search-input { - border-color: #98C; - } - } - } - } - - .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { - background: #659; - } + @include dark-theme(#98C, #548, #436, #325); } diff --git a/app/assets/stylesheets/themes/ui_gray.scss b/app/assets/stylesheets/themes/ui_gray.scss index 8df08ccaeec..9257e5f4d40 100644 --- a/app/assets/stylesheets/themes/ui_gray.scss +++ b/app/assets/stylesheets/themes/ui_gray.scss @@ -1,32 +1,6 @@ /** - * This file represent some UI that can be changed - * during web app restyle or theme select. - * - * Next items should be placed there - * - link colors - * - header restyles - * + * Gray GitLab UI theme */ .ui_gray { - /* - * Application Header - * - */ - header { - @extend .header-dark; - &.navbar-gitlab { - .navbar-inner { - background: #373737; - border-bottom: 1px solid #272727; - .app_logo, .navbar-toggle { - &:hover { - background-color: #272727; - } - } - .app_logo { - background-color: #222; - } - } - } - } + @include dark-theme(#979797, #373737, #272727, #222222); } diff --git a/app/assets/stylesheets/themes/ui_mars.scss b/app/assets/stylesheets/themes/ui_mars.scss index b08cbda6c4f..4caf5843d9b 100644 --- a/app/assets/stylesheets/themes/ui_mars.scss +++ b/app/assets/stylesheets/themes/ui_mars.scss @@ -1,38 +1,6 @@ /** - * This file represent some UI that can be changed - * during web app restyle or theme select. - * - * Next items should be placed there - * - link colors - * - header restyles - * + * Classic GitLab UI theme */ .ui_mars { - /* - * Application Header - * - */ - header { - @extend .header-dark; - &.navbar-gitlab { - .navbar-inner { - background: #474D57; - border-bottom: 1px solid #373D47; - .app_logo, .navbar-toggle { - &:hover { - background-color: #373D47; - } - } - .app_logo { - background-color: #24272D; - } - .nav > li > a { - color: #979DA7; - } - .search-input { - border-color: #979DA7; - } - } - } - } + @include dark-theme(#979DA7, #474D57, #373D47, #24272D); } diff --git a/app/assets/stylesheets/themes/ui_modern.scss b/app/assets/stylesheets/themes/ui_modern.scss index 34f39614ca4..70449882317 100644 --- a/app/assets/stylesheets/themes/ui_modern.scss +++ b/app/assets/stylesheets/themes/ui_modern.scss @@ -1,42 +1,6 @@ /** - * This file represent some UI that can be changed - * during web app restyle or theme select. - * - * Next items should be placed there - * - link colors - * - header restyles - * + * Modern GitLab UI theme */ .ui_modern { - /* - * Application Header - * - */ - header { - @extend .header-dark; - &.navbar-gitlab { - .navbar-inner { - background: #019875; - border-bottom: 1px solid #019875; - .app_logo, .navbar-toggle { - &:hover { - background-color: #018865; - } - } - .app_logo { - background-color: #017855; - } - .nav > li > a { - color: #ADC; - } - .search-input { - border-color: #8ba; - } - } - } - } - - .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { - background: #019875; - } + @include dark-theme(#ADC, #019875, #018865, #017855); } diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 7458542fc73..2b0c500e97a 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -26,6 +26,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :signup_enabled, :signin_enabled, :gravatar_enabled, + :twitter_sharing_enabled, :sign_in_text, :home_page_url ) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 65dc027c8eb..e338abeac4c 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController @groups = Group.all @groups = @groups.sort(@sort = params[:sort]) @groups = @groups.search(params[:name]) if params[:name].present? - @groups = @groups.page(params[:page]).per(20) + @groups = @groups.page(params[:page]).per(PER_PAGE) end def show - @members = @group.members.order("access_level DESC").page(params[:members_page]).per(30) - @projects = @group.projects.page(params[:projects_page]).per(30) + @members = @group.members.order("access_level DESC").page(params[:members_page]).per(PER_PAGE) + @projects = @group.projects.page(params[:projects_page]).per(PER_PAGE) end def new diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 7c2388e81be..5176a8399ae 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -11,27 +11,30 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.abandoned if params[:abandoned].present? @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) + @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE) end def show if @group - @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(30) + @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(PER_PAGE) end - @project_members = @project.project_members.page(params[:project_members_page]).per(30) + @project_members = @project.project_members.page(params[:project_members_page]).per(PER_PAGE) end def transfer ::Projects::TransferService.new(@project, current_user, params.dup).execute - redirect_to [:admin, @project.reload] + @project.reload + redirect_to admin_namespace_project_path(@project.namespace, @project) end protected def project - @project = Project.find_with_namespace(params[:id]) + @project = Project.find_with_namespace( + [params[:namespace_id], '/', params[:id]].join('') + ) @project || render_404 end diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb new file mode 100644 index 00000000000..44a3f1379d8 --- /dev/null +++ b/app/controllers/admin/services_controller.rb @@ -0,0 +1,52 @@ +class Admin::ServicesController < Admin::ApplicationController + before_filter :service, only: [:edit, :update] + + def index + @services = services_templates + end + + def edit + unless service.present? + redirect_to admin_application_settings_services_path, + alert: "Service is unknown or it doesn't exist" + end + end + + def update + if service.update_attributes(application_services_params[:service]) + redirect_to admin_application_settings_services_path, + notice: 'Application settings saved successfully' + else + render :edit + end + end + + private + + def services_templates + templates = [] + + Service.available_services_names.each do |service_name| + service_template = service_name.concat("_service").camelize.constantize + templates << service_template.where(template: true).first_or_create + end + + templates + end + + def service + @service ||= Service.where(id: params[:id], template: true).first + end + + def application_services_params + params.permit(:id, + service: [ + :title, :token, :type, :active, :api_key, :subdomain, + :room, :recipients, :project_url, :webhook, + :user_key, :device, :priority, :sound, :bamboo_url, :username, :password, + :build_key, :server, :teamcity_url, :build_type, + :description, :issues_url, :new_issue_url, :restrict_to_branch, + :send_from_committer_email, :disable_diffs + ]) + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 232f30b759d..693970e5349 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -24,7 +24,7 @@ class Admin::UsersController < Admin::ApplicationController def block if user.block - redirect_to :back, alert: "Successfully blocked" + redirect_to :back, notice: "Successfully blocked" else redirect_to :back, alert: "Error occurred. User was not blocked" end @@ -32,7 +32,7 @@ class Admin::UsersController < Admin::ApplicationController def unblock if user.activate - redirect_to :back, alert: "Successfully unblocked" + redirect_to :back, notice: "Successfully unblocked" else redirect_to :back, alert: "Error occurred. User was not unblocked" end @@ -121,7 +121,7 @@ class Admin::UsersController < Admin::ApplicationController params.require(:user).permit( :email, :remember_me, :bio, :name, :username, :skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password, - :extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key, + :extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key, :hide_no_password, :projects_limit, :can_create_group, :admin, :key_id ) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6553027b430..e284f31f7ee 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,6 +2,9 @@ require 'gon' class ApplicationController < ActionController::Base include Gitlab::CurrentSettings + include GitlabRoutingHelper + + PER_PAGE = 20 before_filter :authenticate_user_from_token! before_filter :authenticate_user! @@ -16,6 +19,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception helper_method :abilities, :can?, :current_application_settings + helper_method :github_import_enabled?, :gitlab_import_enabled?, :bitbucket_import_enabled? rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -93,6 +97,7 @@ class ApplicationController < ActionController::Base def project unless @project + namespace = params[:namespace_id] id = params[:project_id] || params[:id] # Redirect from @@ -104,7 +109,7 @@ class ApplicationController < ActionController::Base redirect_to request.original_url.gsub(/\.git\Z/, '') and return end - @project = Project.find_with_namespace(id) + @project = Project.find_with_namespace("#{namespace}/#{id}") if @project and can?(current_user, :read_project, @project) @project @@ -121,7 +126,8 @@ class ApplicationController < ActionController::Base def repository @repository ||= project.repository - rescue Grit::NoSuchPathError + rescue Grit::NoSuchPathError(e) + log_exception(e) nil end @@ -311,4 +317,16 @@ class ApplicationController < ActionController::Base set_filter_values(merge_requests) merge_requests end + + def github_import_enabled? + OauthHelper.enabled_oauth_providers.include?(:github) + end + + def gitlab_import_enabled? + OauthHelper.enabled_oauth_providers.include?(:gitlab) + end + + def bitbucket_import_enabled? + OauthHelper.enabled_oauth_providers.include?(:bitbucket) && Gitlab::BitbucketImport.public_key.present? + end end diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index ce9dd50df67..b827639978c 100644 --- a/app/controllers/profiles/groups_controller.rb +++ b/app/controllers/dashboard/groups_controller.rb @@ -1,15 +1,13 @@ -class Profiles::GroupsController < ApplicationController - layout "profile" - +class Dashboard::GroupsController < ApplicationController def index - @user_groups = current_user.group_members.page(params[:page]).per(20) + @user_groups = current_user.group_members.page(params[:page]).per(PER_PAGE) end def leave @users_group = group.group_members.where(user_id: current_user.id).first if can?(current_user, :destroy, @users_group) @users_group.destroy - redirect_to(profile_groups_path, info: "You left #{group.name} group.") + redirect_to(dashboard_groups_path, info: "You left #{group.name} group.") else return render_403 end diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb new file mode 100644 index 00000000000..cb51792df16 --- /dev/null +++ b/app/controllers/dashboard/milestones_controller.rb @@ -0,0 +1,34 @@ +class Dashboard::MilestonesController < ApplicationController + before_filter :load_projects + + def index + project_milestones = case params[:state] + when 'all'; state + when 'closed'; state('closed') + else state('active') + end + @dashboard_milestones = Milestones::GroupService.new(project_milestones).execute + @dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(PER_PAGE) + end + + def show + project_milestones = Milestone.where(project_id: @projects).order("due_date ASC") + @dashboard_milestone = Milestones::GroupService.new(project_milestones).milestone(title) + end + + private + + def load_projects + @projects = current_user.authorized_projects.sorted_by_activity.non_archived + end + + def title + params[:title] + end + + def state(state = nil) + conditions = { project_id: @projects } + conditions.reverse_merge!(state: state) if state + Milestone.where(conditions).order("title ASC") + end +end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb new file mode 100644 index 00000000000..56e6fcc41ca --- /dev/null +++ b/app/controllers/dashboard/projects_controller.rb @@ -0,0 +1,27 @@ +class Dashboard::ProjectsController < ApplicationController + before_filter :event_filter + + def starred + @projects = current_user.starred_projects + @projects = @projects.includes(:namespace, :forked_from_project, :tags) + @projects = @projects.sort(@sort = params[:sort]) + @groups = [] + + respond_to do |format| + format.html + + format.json do + load_events + pager_json("events/_events", @events.count) + end + end + end + + private + + def load_events + @events = Event.in_projects(@projects.pluck(:id)) + @events = @event_filter.apply_filter(@events).with_associations + @events = @events.limit(20).offset(params[:offset] || 0) + end +end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 9e59264e418..9bd853ed5c7 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -5,62 +5,33 @@ class DashboardController < ApplicationController before_filter :event_filter, only: :show def show - # Fetch only 30 projects. - # If user needs more - point to Dashboard#projects page - @projects_limit = 30 - - @groups = current_user.authorized_groups.order_name_asc - @has_authorized_projects = @projects.count > 0 - @projects_count = @projects.count - @projects = @projects.limit(@projects_limit) - - @events = Event.in_projects(current_user.authorized_projects.pluck(:id)) - @events = @event_filter.apply_filter(@events) - @events = @events.limit(20).offset(params[:offset] || 0) - + @projects = @projects.includes(:namespace) @last_push = current_user.recent_push - @publicish_project_count = Project.publicish(current_user).count - respond_to do |format| format.html - format.json { pager_json("events/_events", @events.count) } - format.atom { render layout: false } - end - end - - def projects - @projects = case params[:scope] - when 'personal' then - current_user.namespace.projects - when 'joined' then - current_user.authorized_projects.joined(current_user) - when 'owned' then - current_user.owned_projects - else - current_user.authorized_projects - end - @projects = @projects.where(namespace_id: Group.find_by(name: params[:group])) if params[:group].present? - @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? - @projects = @projects.includes(:namespace) - @projects = @projects.tagged_with(params[:tag]) if params[:tag].present? - @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.page(params[:page]).per(30) + format.json do + load_events + pager_json("events/_events", @events.count) + end - @tags = current_user.authorized_projects.tags_on(:tags) - @groups = current_user.authorized_groups + format.atom do + load_events + render layout: false + end + end end def merge_requests @merge_requests = get_merge_requests_collection - @merge_requests = @merge_requests.page(params[:page]).per(20) + @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) @merge_requests = @merge_requests.preload(:author, :target_project) end def issues @issues = get_issues_collection - @issues = @issues.page(params[:page]).per(20) + @issues = @issues.page(params[:page]).per(PER_PAGE) @issues = @issues.preload(:author, :project) respond_to do |format| @@ -74,4 +45,10 @@ class DashboardController < ApplicationController def load_projects @projects = current_user.authorized_projects.sorted_by_activity.non_archived end + + def load_events + @events = Event.in_projects(current_user.authorized_projects.pluck(:id)) + @events = @event_filter.apply_filter(@events).with_associations + @events = @events.limit(20).offset(params[:offset] || 0) + end end diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index ada7031fea4..c51a4a211a6 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -8,6 +8,6 @@ class Explore::GroupsController < ApplicationController @groups = GroupsFinder.new.execute(current_user) @groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.sort(@sort = params[:sort]) - @groups = @groups.page(params[:page]).per(20) + @groups = @groups.page(params[:page]).per(PER_PAGE) end end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index d75fd8e72fa..b295f295bb1 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -6,19 +6,22 @@ class Explore::ProjectsController < ApplicationController def index @projects = ProjectsFinder.new.execute(current_user) + @tags = @projects.tags_on(:tags) + @projects = @projects.tagged_with(params[:tag]) if params[:tag].present? + @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? @projects = @projects.search(params[:search]) if params[:search].present? @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.includes(:namespace).page(params[:page]).per(20) + @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) end def trending @trending_projects = TrendingProjectsFinder.new.execute(current_user) - @trending_projects = @trending_projects.page(params[:page]).per(10) + @trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE) end def starred @starred_projects = ProjectsFinder.new.execute(current_user) - @starred_projects = @starred_projects.order('star_count DESC') - @starred_projects = @starred_projects.page(params[:page]).per(10) + @starred_projects = @starred_projects.reorder('star_count DESC') + @starred_projects = @starred_projects.page(params[:page]).per(PER_PAGE) end end diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb deleted file mode 100644 index 9671245d3f4..00000000000 --- a/app/controllers/files_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -class FilesController < ApplicationController - def download - note = Note.find(params[:id]) - uploader = note.attachment - - if uploader.file_storage? - if can?(current_user, :read_project, note.project) - disposition = uploader.image? ? 'inline' : 'attachment' - send_file uploader.file.path, disposition: disposition - else - not_found! - end - else - redirect_to uploader.url - end - end -end diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb new file mode 100644 index 00000000000..7f27f2bb734 --- /dev/null +++ b/app/controllers/groups/application_controller.rb @@ -0,0 +1,10 @@ +class Groups::ApplicationController < ApplicationController + + private + + def authorize_admin_group! + unless can?(current_user, :manage_group, group) + return render_404 + end + end +end diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index ca88d033878..b083cf5d8c5 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,4 +1,4 @@ -class Groups::GroupMembersController < ApplicationController +class Groups::GroupMembersController < Groups::ApplicationController before_filter :group # Authorize @@ -37,12 +37,6 @@ class Groups::GroupMembersController < ApplicationController @group ||= Group.find_by(path: params[:group_id]) end - def authorize_admin_group! - unless can?(current_user, :manage_group, group) - return render_404 - end - end - def member_params params.require(:group_member).permit(:access_level, :user_id) end diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 860d8e03922..c46b8fff88f 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -4,13 +4,13 @@ class Groups::MilestonesController < ApplicationController before_filter :authorize_group_milestone!, only: :update def index - project_milestones = case params[:status] - when 'all'; status - when 'closed'; status('closed') - else status('active') + project_milestones = case params[:state] + when 'all'; state + when 'closed'; state('closed') + else state('active') end @group_milestones = Milestones::GroupService.new(project_milestones).execute - @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(30) + @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE) end def show @@ -44,7 +44,7 @@ class Groups::MilestonesController < ApplicationController params[:title] end - def status(state = nil) + def state(state = nil) conditions = { project_id: group.projects } conditions.reverse_merge!(state: state) if state Milestone.where(conditions).order("title ASC") diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index aad3709090e..7e336803fbb 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,4 +1,4 @@ -class GroupsController < ApplicationController +class GroupsController < Groups::ApplicationController skip_before_filter :authenticate_user!, only: [:show, :issues, :members, :merge_requests] respond_to :html before_filter :group, except: [:new, :create] @@ -10,11 +10,11 @@ class GroupsController < ApplicationController # Load group projects before_filter :load_projects, except: [:new, :create, :projects, :edit, :update] + before_filter :event_filter, only: :show + before_filter :set_title, only: [:new, :create] layout :determine_layout - before_filter :set_title, only: [:new, :create] - def new @group = Group.new end @@ -32,27 +32,33 @@ class GroupsController < ApplicationController end def show - @events = Event.in_projects(project_ids) - @events = event_filter.apply_filter(@events) - @events = @events.limit(20).offset(params[:offset] || 0) @last_push = current_user.recent_push if current_user + @projects = @projects.includes(:namespace) respond_to do |format| format.html - format.json { pager_json("events/_events", @events.count) } - format.atom { render layout: false } + + format.json do + load_events + pager_json("events/_events", @events.count) + end + + format.atom do + load_events + render layout: false + end end end def merge_requests @merge_requests = get_merge_requests_collection - @merge_requests = @merge_requests.page(params[:page]).per(20) + @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) @merge_requests = @merge_requests.preload(:author, :target_project) end def issues @issues = get_issues_collection - @issues = @issues.page(params[:page]).per(20) + @issues = @issues.page(params[:page]).per(PER_PAGE) @issues = @issues.preload(:author, :project) respond_to do |format| @@ -126,12 +132,6 @@ class GroupsController < ApplicationController end end - def authorize_admin_group! - unless can?(current_user, :manage_group, group) - return render_404 - end - end - def set_title @title = 'New Group' end @@ -149,4 +149,10 @@ class GroupsController < ApplicationController def group_params params.require(:group).permit(:name, :description, :path, :avatar) end + + def load_events + @events = Event.in_projects(project_ids) + @events = event_filter.apply_filter(@events).with_associations + @events = @events.limit(20).offset(params[:offset] || 0) + end end diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index fc498559d6b..c4d620d87b1 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -15,4 +15,7 @@ class HelpController < ApplicationController def shortcuts end + + def ui + end end diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb index 4df171dbcfe..edb8bd4160b 100644 --- a/app/controllers/import/base_controller.rb +++ b/app/controllers/import/base_controller.rb @@ -3,19 +3,17 @@ class Import::BaseController < ApplicationController private def get_or_create_namespace - existing_namespace = Namespace.find_by("path = ? OR name = ?", @target_namespace, @target_namespace) - - if existing_namespace - if existing_namespace.owner == current_user - namespace = existing_namespace - else + begin + namespace = Group.create!(name: @target_namespace, path: @target_namespace, owner: current_user) + namespace.add_owner(current_user) + rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid + namespace = Namespace.find_by_path_or_name(@target_namespace) + unless namespace.owner == current_user @already_been_taken = true return false end - else - namespace = Group.create(name: @target_namespace, path: @target_namespace, owner: current_user) - namespace.add_owner(current_user) - namespace end + + namespace end end diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb new file mode 100644 index 00000000000..83ebc5fddca --- /dev/null +++ b/app/controllers/import/bitbucket_controller.rb @@ -0,0 +1,79 @@ +class Import::BitbucketController < Import::BaseController + before_filter :verify_bitbucket_import_enabled + before_filter :bitbucket_auth, except: :callback + + rescue_from OAuth::Error, with: :bitbucket_unauthorized + + def callback + request_token = session.delete(:oauth_request_token) + raise "Session expired!" if request_token.nil? + + request_token.symbolize_keys! + + access_token = client.get_token(request_token, params[:oauth_verifier], callback_import_bitbucket_url) + + current_user.bitbucket_access_token = access_token.token + current_user.bitbucket_access_token_secret = access_token.secret + + current_user.save + redirect_to status_import_bitbucket_url + end + + def status + @repos = client.projects + + @already_added_projects = current_user.created_projects.where(import_type: "bitbucket") + already_added_projects_names = @already_added_projects.pluck(:import_source) + + @repos.to_a.reject!{ |repo| already_added_projects_names.include? "#{repo["owner"]}/#{repo["slug"]}" } + end + + def jobs + jobs = current_user.created_projects.where(import_type: "bitbucket").to_json(only: [:id, :import_status]) + render json: jobs + end + + def create + @repo_id = params[:repo_id] || "" + repo = client.project(@repo_id.gsub("___", "/")) + @target_namespace = params[:new_namespace].presence || repo["owner"] + @project_name = repo["slug"] + + namespace = get_or_create_namespace || (render and return) + + unless Gitlab::BitbucketImport::KeyAdder.new(repo, current_user).execute + @access_denied = true + render + return + end + + @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, current_user).execute + end + + private + + def client + @client ||= Gitlab::BitbucketImport::Client.new(current_user.bitbucket_access_token, current_user.bitbucket_access_token_secret) + end + + def verify_bitbucket_import_enabled + not_found! unless bitbucket_import_enabled? + end + + def bitbucket_auth + if current_user.bitbucket_access_token.blank? + go_to_bitbucket_for_permissions + end + end + + def go_to_bitbucket_for_permissions + request_token = client.request_token(callback_import_bitbucket_url) + session[:oauth_request_token] = request_token + + redirect_to client.authorize_url(request_token, callback_import_bitbucket_url) + end + + def bitbucket_unauthorized + go_to_bitbucket_for_permissions + end +end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index c869c7c86f3..8650b6464dc 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -1,4 +1,5 @@ class Import::GithubController < Import::BaseController + before_filter :verify_github_import_enabled before_filter :github_auth, except: :callback rescue_from Octokit::Unauthorized, with: :github_unauthorized @@ -13,7 +14,7 @@ class Import::GithubController < Import::BaseController def status @repos = client.repos client.orgs.each do |org| - @repos += client.repos(org.login) + @repos += client.org_repos(org.login) end @already_added_projects = current_user.created_projects.where(import_type: "github") @@ -44,6 +45,10 @@ class Import::GithubController < Import::BaseController @client ||= Gitlab::GithubImport::Client.new(current_user.github_access_token) end + def verify_github_import_enabled + not_found! unless github_import_enabled? + end + def github_auth if current_user.github_access_token.blank? go_to_github_for_permissions diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb index a51ea36aff8..e979dad4b11 100644 --- a/app/controllers/import/gitlab_controller.rb +++ b/app/controllers/import/gitlab_controller.rb @@ -1,4 +1,5 @@ class Import::GitlabController < Import::BaseController + before_filter :verify_gitlab_import_enabled before_filter :gitlab_auth, except: :callback rescue_from OAuth2::Error, with: :gitlab_unauthorized @@ -16,7 +17,7 @@ class Import::GitlabController < Import::BaseController @already_added_projects = current_user.created_projects.where(import_type: "gitlab") already_added_projects_names = @already_added_projects.pluck(:import_source) - @repos.to_a.reject!{ |repo| already_added_projects_names.include? repo["path_with_namespace"] } + @repos = @repos.to_a.reject{ |repo| already_added_projects_names.include? repo["path_with_namespace"] } end def jobs @@ -41,6 +42,10 @@ class Import::GitlabController < Import::BaseController @client ||= Gitlab::GitlabImport::Client.new(current_user.gitlab_access_token) end + def verify_gitlab_import_enabled + not_found! unless gitlab_import_enabled? + end + def gitlab_auth if current_user.gitlab_access_token.blank? go_to_gitlab_for_permissions diff --git a/app/controllers/import/gitorious_controller.rb b/app/controllers/import/gitorious_controller.rb new file mode 100644 index 00000000000..6067a87ee04 --- /dev/null +++ b/app/controllers/import/gitorious_controller.rb @@ -0,0 +1,43 @@ +class Import::GitoriousController < Import::BaseController + + def new + redirect_to client.authorize_url(callback_import_gitorious_url) + end + + def callback + session[:gitorious_repos] = params[:repos] + redirect_to status_import_gitorious_url + end + + def status + @repos = client.repos + + @already_added_projects = current_user.created_projects.where(import_type: "gitorious") + already_added_projects_names = @already_added_projects.pluck(:import_source) + + @repos.reject! { |repo| already_added_projects_names.include? repo.full_name } + end + + def jobs + jobs = current_user.created_projects.where(import_type: "gitorious").to_json(only: [:id, :import_status]) + render json: jobs + end + + def create + @repo_id = params[:repo_id] + repo = client.repo(@repo_id) + @target_namespace = params[:new_namespace].presence || repo.namespace + @project_name = repo.name + + namespace = get_or_create_namespace || (render and return) + + @project = Gitlab::GitoriousImport::ProjectCreator.new(repo, namespace, current_user).execute + end + + private + + def client + @client ||= Gitlab::GitoriousImport::Client.new(session[:gitorious_repos]) + end + +end diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb index 1191ce47eba..0c614969a3f 100644 --- a/app/controllers/profiles/passwords_controller.rb +++ b/app/controllers/profiles/passwords_controller.rb @@ -11,7 +11,7 @@ class Profiles::PasswordsController < ApplicationController end def create - unless @user.valid_password?(user_params[:current_password]) + unless @user.password_automatically_set || @user.valid_password?(user_params[:current_password]) redirect_to new_profile_password_path, alert: 'You must provide a valid current password' return end @@ -21,7 +21,8 @@ class Profiles::PasswordsController < ApplicationController result = @user.update_attributes( password: new_password, - password_confirmation: new_password_confirmation + password_confirmation: new_password_confirmation, + password_automatically_set: false ) if result @@ -39,8 +40,9 @@ class Profiles::PasswordsController < ApplicationController password_attributes = user_params.select do |key, value| %w(password password_confirmation).include?(key.to_s) end + password_attributes[:password_automatically_set] = false - unless @user.valid_password?(user_params[:current_password]) + unless @user.password_automatically_set || @user.valid_password?(user_params[:current_password]) redirect_to edit_profile_password_path, alert: 'You must provide a valid current password' return end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index c0b7e2223a2..1b9a86ee42c 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -16,6 +16,7 @@ class ProfilesController < ApplicationController def applications @applications = current_user.oauth_applications @authorized_tokens = current_user.oauth_authorized_tokens + @authorized_apps = @authorized_tokens.map(&:application).uniq end def update @@ -42,7 +43,7 @@ class ProfilesController < ApplicationController end def history - @events = current_user.recent_events.page(params[:page]).per(20) + @events = current_user.recent_events.page(params[:page]).per(PER_PAGE) end def update_username @@ -67,7 +68,7 @@ class ProfilesController < ApplicationController params.require(:user).permit( :email, :password, :password_confirmation, :bio, :name, :username, :skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, - :avatar, :hide_no_ssh_key, + :avatar, :hide_no_ssh_key, :hide_no_password ) end end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 7e4580017dd..4719933394f 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -8,7 +8,8 @@ class Projects::ApplicationController < ApplicationController # for non-signed users if !current_user id = params[:project_id] || params[:id] - @project = Project.find_with_namespace(id) + project_with_namespace = "#{params[:namespace_id]}/#{id}" + @project = Project.find_with_namespace(project_with_namespace) return if @project && @project.public? end @@ -26,7 +27,10 @@ class Projects::ApplicationController < ApplicationController def require_branch_head unless @repository.branch_names.include?(@ref) - redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch" + redirect_to( + namespace_project_tree_path(@project.namespace, @project, @ref), + notice: "This action is not allowed unless you are on top of a branch" + ) end end end diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 106f21b83e6..489a6ae5666 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -2,9 +2,9 @@ class Projects::BlameController < Projects::ApplicationController include ExtractsPath + before_filter :require_non_empty_project before_filter :assign_ref_vars before_filter :authorize_download_code! - before_filter :require_non_empty_project def show @blob = @repository.blob_at(@commit.id, @path) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index dccb96ba1d1..4b7eb4df298 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -1,12 +1,13 @@ # Controller for viewing a file's blame class Projects::BlobController < Projects::ApplicationController include ExtractsPath + include ActionView::Helpers::SanitizeHelper # Raised when given an invalid file path class InvalidPathError < StandardError; end - before_filter :authorize_download_code! before_filter :require_non_empty_project, except: [:new, :create] + before_filter :authorize_download_code! before_filter :authorize_push_code!, only: [:destroy] before_filter :assign_blob_vars before_filter :commit, except: [:new, :create] @@ -21,11 +22,18 @@ class Projects::BlobController < Projects::ApplicationController def create file_path = File.join(@path, File.basename(params[:file_name])) - result = Files::CreateService.new(@project, current_user, params, @ref, file_path).execute + result = Files::CreateService.new( + @project, + current_user, + params.merge(new_branch: sanitized_new_branch_name), + @ref, + file_path + ).execute if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - redirect_to project_blob_path(@project, File.join(@ref, file_path)) + ref = sanitized_new_branch_name.presence || @ref + redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(ref, file_path)) else flash[:alert] = result[:message] render :new @@ -41,7 +49,13 @@ class Projects::BlobController < Projects::ApplicationController def update result = Files::UpdateService. - new(@project, current_user, params, @ref, @path).execute + new( + @project, + current_user, + params.merge(new_branch: sanitized_new_branch_name), + @ref, + @path + ).execute if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" @@ -70,7 +84,8 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - redirect_to project_tree_path(@project, @ref) + redirect_to namespace_project_tree_path(@project.namespace, @project, + @ref) else flash[:alert] = result[:message] render :show @@ -102,7 +117,7 @@ class Projects::BlobController < Projects::ApplicationController else if tree = @repository.tree(@commit.id, @path) if tree.entries.any? - redirect_to project_tree_path(@project, File.join(@ref, @path)) and return + redirect_to namespace_project_tree_path(@project.namespace, @project, File.join(@ref, @path)) and return end end @@ -128,10 +143,12 @@ class Projects::BlobController < Projects::ApplicationController def after_edit_path @after_edit_path ||= if from_merge_request - diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) + + diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + "#file-path-#{hexdigest(@path)}" + elsif sanitized_new_branch_name.present? + namespace_project_blob_path(@project.namespace, @project, File.join(sanitized_new_branch_name, @path)) else - project_blob_path(@project, @id) + namespace_project_blob_path(@project.namespace, @project, @id) end end @@ -139,4 +156,8 @@ class Projects::BlobController < Projects::ApplicationController # If blob edit was initiated from merge request page @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id]) end + + def sanitized_new_branch_name + @new_branch ||= sanitize(strip_tags(params[:new_branch])) + end end diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index cff1a907dc2..f049e96e61d 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -2,14 +2,13 @@ class Projects::BranchesController < Projects::ApplicationController include ActionView::Helpers::SanitizeHelper # Authorize before_filter :require_non_empty_project - before_filter :authorize_download_code! before_filter :authorize_push_code!, only: [:create, :destroy] def index @sort = params[:sort] || 'name' @branches = @repository.branches_sorted_by(@sort) - @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(30) + @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) end def recent @@ -24,7 +23,8 @@ class Projects::BranchesController < Projects::ApplicationController if result[:status] == :success @branch = result[:branch] - redirect_to project_tree_path(@project, @branch.name) + redirect_to namespace_project_tree_path(@project.namespace, @project, + @branch.name) else @error = result[:message] render action: 'new' @@ -36,7 +36,10 @@ class Projects::BranchesController < Projects::ApplicationController @branch_name = params[:id] respond_to do |format| - format.html { redirect_to project_branches_path(@project) } + format.html do + redirect_to namespace_project_branches_path(@project.namespace, + @project) + end format.js end end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 96a782bdf7a..87e39f1363a 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -3,8 +3,8 @@ # Not to be confused with CommitsController, plural. class Projects::CommitController < Projects::ApplicationController # Authorize - before_filter :authorize_download_code! before_filter :require_non_empty_project + before_filter :authorize_download_code! before_filter :commit def show diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index b133afe44b5..4b6ab437476 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -3,9 +3,9 @@ require "base64" class Projects::CommitsController < Projects::ApplicationController include ExtractsPath + before_filter :require_non_empty_project before_filter :assign_ref_vars before_filter :authorize_download_code! - before_filter :require_non_empty_project def show @repo = @project.repository diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index ffb8c2e4af1..146808fa562 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -1,7 +1,7 @@ class Projects::CompareController < Projects::ApplicationController # Authorize - before_filter :authorize_download_code! before_filter :require_non_empty_project + before_filter :authorize_download_code! def index end @@ -25,6 +25,7 @@ class Projects::CompareController < Projects::ApplicationController end def create - redirect_to project_compare_path(@project, params[:from], params[:to]) + redirect_to namespace_project_compare_path(@project.namespace, @project, + params[:from], params[:to]) end end diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 024b9520d30..b7cc305899c 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -25,7 +25,8 @@ class Projects::DeployKeysController < Projects::ApplicationController @key = DeployKey.new(deploy_key_params) if @key.valid? && @project.deploy_keys << @key - redirect_to project_deploy_keys_path(@project) + redirect_to namespace_project_deploy_keys_path(@project.namespace, + @project) else render "new" end @@ -44,13 +45,15 @@ class Projects::DeployKeysController < Projects::ApplicationController def enable @project.deploy_keys << available_keys.find(params[:id]) - redirect_to project_deploy_keys_path(@project) + redirect_to namespace_project_deploy_keys_path(@project.namespace, + @project) end def disable @project.deploy_keys_projects.where(deploy_key_id: params[:id]).last.destroy - redirect_to project_deploy_keys_path(@project) + redirect_to namespace_project_deploy_keys_path(@project.namespace, + @project) end protected diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index a0481d11582..21a151a426e 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -1,7 +1,7 @@ class Projects::ForksController < Projects::ApplicationController # Authorize - before_filter :authorize_download_code! before_filter :require_non_empty_project + before_filter :authorize_download_code! def new @namespaces = current_user.manageable_namespaces @@ -9,11 +9,14 @@ class Projects::ForksController < Projects::ApplicationController end def create - namespace = Namespace.find(params[:namespace_id]) + namespace = Namespace.find(params[:namespace_key]) @forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute if @forked_project.saved? && @forked_project.forked? - redirect_to(@forked_project, notice: 'Project was successfully forked.') + redirect_to( + namespace_project_path(@forked_project.namespace, @forked_project), + notice: 'Project was successfully forked.' + ) else @title = 'Fork project' render :error diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 4a318cb7d56..752474b4a4c 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -1,7 +1,7 @@ class Projects::GraphsController < Projects::ApplicationController # Authorize - before_filter :authorize_download_code! before_filter :require_non_empty_project + before_filter :authorize_download_code! def show respond_to do |format| diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 2d6c3111192..ba95bb13e1f 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -16,7 +16,7 @@ class Projects::HooksController < Projects::ApplicationController @hook.save if @hook.valid? - redirect_to project_hooks_path(@project) + redirect_to namespace_project_hooks_path(@project.namespace, @project) else @hooks = @project.hooks.select(&:persisted?) render :index @@ -43,7 +43,7 @@ class Projects::HooksController < Projects::ApplicationController def destroy hook.destroy - redirect_to project_hooks_path(@project) + redirect_to namespace_project_hooks_path(@project.namespace, @project) end private diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index b8350642804..b64491b4666 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -20,15 +20,16 @@ class Projects::ImportsController < Projects::ApplicationController end end - redirect_to project_import_path(@project) + redirect_to namespace_project_import_path(@project.namespace, @project) end def show unless @project.import_in_progress? if @project.import_finished? - redirect_to(@project) and return + redirect_to(project_path(@project)) and return else - redirect_to new_project_import_path(@project) and return + redirect_to new_namespace_project_import_path(@project.namespace, + @project) && return end end end @@ -36,14 +37,15 @@ class Projects::ImportsController < Projects::ApplicationController private def require_no_repo - if @project.repository_exists? - redirect_to(@project) and return + if @project.repository_exists? && !@project.import_in_progress? + redirect_to(namespace_project_path(@project.namespace, @project)) and return end end def redirect_if_progress if @project.import_in_progress? - redirect_to project_import_path(@project) and return + redirect_to namespace_project_import_path(@project.namespace, @project) && + return end end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 42e207cf376..4266bcaef16 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController terms = params['issue_search'] @issues = get_issues_collection @issues = @issues.full_search(terms) if terms.present? - @issues = @issues.page(params[:page]).per(20) + @issues = @issues.page(params[:page]).per(PER_PAGE) respond_to do |format| format.html @@ -60,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController respond_to do |format| format.html do if @issue.valid? - redirect_to project_issue_path(@project, @issue) + redirect_to issue_path(@issue) else render :new end @@ -78,7 +78,7 @@ class Projects::IssuesController < Projects::ApplicationController format.js format.html do if @issue.valid? - redirect_to [@project, @issue] + redirect_to issue_path(@issue) else render :edit end @@ -93,7 +93,7 @@ class Projects::IssuesController < Projects::ApplicationController end def bulk_update - result = Issues::BulkUpdateService.new(project, current_user, params).execute + result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute redirect_to :back, notice: "#{result[:count]} issues updated" end @@ -128,7 +128,7 @@ class Projects::IssuesController < Projects::ApplicationController issue = @project.issues.find_by(id: params[:id]) if issue - redirect_to project_issue_path(@project, issue) + redirect_to issue_path(issue) return else raise ActiveRecord::RecordNotFound.new @@ -141,4 +141,13 @@ class Projects::IssuesController < Projects::ApplicationController :milestone_id, :state_event, :task_num, label_ids: [] ) end + + def bulk_update_params + params.require(:update).permit( + :issues_ids, + :assignee_id, + :milestone_id, + :state_event + ) + end end diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index b61fef3b627..207a01ed3b0 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -7,7 +7,7 @@ class Projects::LabelsController < Projects::ApplicationController respond_to :js, :html def index - @labels = @project.labels.page(params[:page]).per(20) + @labels = @project.labels.page(params[:page]).per(PER_PAGE) end def new @@ -18,7 +18,7 @@ class Projects::LabelsController < Projects::ApplicationController @label = @project.labels.create(label_params) if @label.valid? - redirect_to project_labels_path(@project) + redirect_to namespace_project_labels_path(@project.namespace, @project) else render 'new' end @@ -29,7 +29,7 @@ class Projects::LabelsController < Projects::ApplicationController def update if @label.update_attributes(label_params) - redirect_to project_labels_path(@project) + redirect_to namespace_project_labels_path(@project.namespace, @project) else render 'edit' end @@ -39,11 +39,12 @@ class Projects::LabelsController < Projects::ApplicationController Gitlab::IssuesLabels.generate(@project) if params[:redirect] == 'issues' - redirect_to project_issues_path(@project) + redirect_to namespace_project_issues_path(@project.namespace, @project) elsif params[:redirect] == 'merge_requests' - redirect_to project_merge_requests_path(@project) + redirect_to namespace_project_merge_requests_path(@project.namespace, + @project) else - redirect_to project_labels_path(@project) + redirect_to namespace_project_labels_path(@project.namespace, @project) end end @@ -51,7 +52,10 @@ class Projects::LabelsController < Projects::ApplicationController @label.destroy respond_to do |format| - format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' } + format.html do + redirect_to(namespace_project_labels_path(@project.namespace, @project), + notice: 'Label was removed') + end format.js end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 01be318ede2..93d79d81661 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -17,8 +17,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort] def index + terms = params['issue_search'] @merge_requests = get_merge_requests_collection - @merge_requests = @merge_requests.page(params[:page]).per(20) + @merge_requests = @merge_requests.full_search(terms) if terms.present? + @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) + + respond_to do |format| + format.html + format.json do + render json: { + html: view_to_html_string("projects/merge_requests/_merge_requests") + } + end + end end def show @@ -78,7 +89,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute if @merge_request.valid? - redirect_to project_merge_request_path(@merge_request.target_project, @merge_request), notice: 'Merge request was successfully created.' + redirect_to(merge_request_path(@merge_request)) else @source_project = @merge_request.source_project @target_project = @merge_request.target_project @@ -93,7 +104,14 @@ class Projects::MergeRequestsController < Projects::ApplicationController respond_to do |format| format.js format.html do - redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.' + redirect_to([@merge_request.target_project.namespace.becomes(Namespace), + @merge_request.target_project, @merge_request]) + end + format.json do + render json: { + saved: @merge_request.valid?, + assignee_avatar_url: @merge_request.assignee.try(:avatar_url) + } end end else diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 95801f8b8fb..b49b549547a 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -18,7 +18,7 @@ class Projects::MilestonesController < Projects::ApplicationController end @milestones = @milestones.includes(:project) - @milestones = @milestones.page(params[:page]).per(20) + @milestones = @milestones.page(params[:page]).per(PER_PAGE) end def new @@ -40,7 +40,8 @@ class Projects::MilestonesController < Projects::ApplicationController @milestone = Milestones::CreateService.new(project, current_user, milestone_params).execute if @milestone.save - redirect_to project_milestone_path(@project, @milestone) + redirect_to namespace_project_milestone_path(@project.namespace, + @project, @milestone) else render "new" end @@ -53,7 +54,8 @@ class Projects::MilestonesController < Projects::ApplicationController format.js format.html do if @milestone.valid? - redirect_to [@project, @milestone] + redirect_to namespace_project_milestone_path(@project.namespace, + @project, @milestone) else render :edit end @@ -67,7 +69,7 @@ class Projects::MilestonesController < Projects::ApplicationController @milestone.destroy respond_to do |format| - format.html { redirect_to project_milestones_path } + format.html { redirect_to namespace_project_milestones_path } format.js { render nothing: true } end end diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index 59f2a745367..83d1c1dacae 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -2,9 +2,9 @@ class Projects::NetworkController < Projects::ApplicationController include ExtractsPath include ApplicationHelper + before_filter :require_non_empty_project before_filter :assign_ref_vars before_filter :authorize_download_code! - before_filter :require_non_empty_project def show respond_to do |format| diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 2f1d631c14a..868629a0bc4 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -3,10 +3,10 @@ class Projects::NotesController < Projects::ApplicationController before_filter :authorize_read_note! before_filter :authorize_write_note!, only: [:create] before_filter :authorize_admin_note!, only: [:update, :destroy] + before_filter :find_current_user_notes, except: [:destroy, :delete_attachment] def index current_fetched_at = Time.now.to_i - @notes = NotesFinder.new.execute(project, current_user, params) notes_json = { notes: [], last_fetched_at: current_fetched_at } @@ -116,4 +116,10 @@ class Projects::NotesController < Projects::ApplicationController :attachment, :line_code, :commit_id ) end + + private + + def find_current_user_notes + @notes = NotesFinder.new.execute(project, current_user, params) + end end diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb index f45df38b87c..ac36ac6fcd3 100644 --- a/app/controllers/projects/protected_branches_controller.rb +++ b/app/controllers/projects/protected_branches_controller.rb @@ -12,7 +12,8 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController def create @project.protected_branches.create(protected_branch_params) - redirect_to project_protected_branches_path(@project) + redirect_to namespace_project_protected_branches_path(@project.namespace, + @project) end def update @@ -37,7 +38,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController @project.protected_branches.find(params[:id]).destroy respond_to do |format| - format.html { redirect_to project_protected_branches_path } + format.html { redirect_to namespace_project_protected_branches_path } format.js { render nothing: true } end end diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index c4ddc32e8c3..b1a029ce696 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -2,9 +2,9 @@ class Projects::RawController < Projects::ApplicationController include ExtractsPath + before_filter :require_non_empty_project before_filter :assign_ref_vars before_filter :authorize_download_code! - before_filter :require_non_empty_project def show @blob = @repository.blob_at(@commit.id, @path) diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index b80472f8eb4..67acf45ab7f 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -1,21 +1,23 @@ class Projects::RefsController < Projects::ApplicationController include ExtractsPath + before_filter :require_non_empty_project before_filter :assign_ref_vars before_filter :authorize_download_code! - before_filter :require_non_empty_project def switch respond_to do |format| format.html do new_path = if params[:destination] == "tree" - project_tree_path(@project, (@id)) + namespace_project_tree_path(@project.namespace, @project, + (@id)) elsif params[:destination] == "blob" - project_blob_path(@project, (@id)) + namespace_project_blob_path(@project.namespace, @project, + (@id)) elsif params[:destination] == "graph" - project_network_path(@project, @id, @options) + namespace_project_network_path(@project.namespace, @project, @id, @options) else - project_commits_path(@project, @id) + namespace_project_commits_path(@project.namespace, @project, @id) end redirect_to new_path diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index 3a90c1c806d..cbb888b25e8 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -1,13 +1,13 @@ class Projects::RepositoriesController < Projects::ApplicationController # Authorize - before_filter :authorize_download_code! before_filter :require_non_empty_project, except: :create + before_filter :authorize_download_code! before_filter :authorize_admin_project!, only: :create def create @project.create_repository - redirect_to @project + redirect_to project_path(@project) end def archive diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index b3110eacc18..570447c746c 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -17,8 +17,11 @@ class Projects::ServicesController < Projects::ApplicationController def update if @service.update_attributes(service_params) - redirect_to edit_project_service_path(@project, @service.to_param), - notice: 'Successfully updated.' + redirect_to( + edit_namespace_project_service_path(@project.namespace, @project, + @service.to_param, notice: + 'Successfully updated.') + ) else render 'edit' end @@ -29,7 +32,7 @@ class Projects::ServicesController < Projects::ApplicationController if @service.execute(data) message = { notice: 'We sent a request to the provided URL' } else - message = { alert: 'We tried to send a request to the provided URL but error occured' } + message = { alert: 'We tried to send a request to the provided URL but an error occured' } end redirect_to :back, message @@ -47,7 +50,10 @@ class Projects::ServicesController < Projects::ApplicationController :room, :recipients, :project_url, :webhook, :user_key, :device, :priority, :sound, :bamboo_url, :username, :password, :build_key, :server, :teamcity_url, :build_type, - :description, :issues_url, :new_issue_url, :restrict_to_branch + :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, + :colorize_messages, :channels, + :push_events, :issues_events, :merge_requests_events, :tag_push_events, + :note_events, :send_from_committer_email, :disable_diffs ) end end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 25c887deafa..6c250e4ffed 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -32,7 +32,8 @@ class Projects::SnippetsController < Projects::ApplicationController @snippet.author = current_user if @snippet.save - redirect_to project_snippet_path(@project, @snippet) + redirect_to namespace_project_snippet_path(@project.namespace, @project, + @snippet) else respond_with(@snippet) end @@ -43,7 +44,7 @@ class Projects::SnippetsController < Projects::ApplicationController def update if @snippet.update_attributes(snippet_params) - redirect_to project_snippet_path(@project, @snippet) + redirect_to namespace_project_snippet_path(@project.namespace, @project, @snippet) else respond_with(@snippet) end @@ -60,7 +61,7 @@ class Projects::SnippetsController < Projects::ApplicationController @snippet.destroy - redirect_to project_snippets_path(@project) + redirect_to namespace_project_snippets_path(@project.namespace, @project) end def raw diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 64b820160d3..c4f27a6d989 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController def index sorted = VersionSorter.rsort(@repository.tag_names) - @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(30) + @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE) end def create @@ -16,7 +16,7 @@ class Projects::TagsController < Projects::ApplicationController if result[:status] == :success @tag = result[:tag] - redirect_to project_tags_path(@project) + redirect_to namespace_project_tags_path(@project.namespace, @project) else @error = result[:message] render action: 'new' @@ -27,11 +27,11 @@ class Projects::TagsController < Projects::ApplicationController tag = @repository.find_tag(params[:id]) if tag && @repository.rm_tag(tag.name) - Event.create_ref_event(@project, current_user, tag, 'rm', 'refs/tags') + EventCreateService.new.push_ref(@project, current_user, tag, 'rm', Gitlab::Git::TAG_REF_PREFIX) end respond_to do |format| - format.html { redirect_to project_tags_path } + format.html { redirect_to namespace_project_tags_path } format.js end end diff --git a/app/controllers/projects/team_members_controller.rb b/app/controllers/projects/team_members_controller.rb index 0791e6080fb..f8a248ed729 100644 --- a/app/controllers/projects/team_members_controller.rb +++ b/app/controllers/projects/team_members_controller.rb @@ -15,14 +15,9 @@ class Projects::TeamMembersController < Projects::ApplicationController def create users = User.where(id: params[:user_ids].split(',')) - @project.team << [users, params[:access_level]] - if params[:redirect_to] - redirect_to params[:redirect_to] - else - redirect_to project_team_index_path(@project) - end + redirect_to namespace_project_team_index_path(@project.namespace, @project) end def update @@ -32,7 +27,7 @@ class Projects::TeamMembersController < Projects::ApplicationController unless @user_project_relation.valid? flash[:alert] = "User should have at least one role" end - redirect_to project_team_index_path(@project) + redirect_to namespace_project_team_index_path(@project.namespace, @project) end def destroy @@ -40,7 +35,10 @@ class Projects::TeamMembersController < Projects::ApplicationController @user_project_relation.destroy respond_to do |format| - format.html { redirect_to project_team_index_path(@project) } + format.html do + redirect_to namespace_project_team_index_path(@project.namespace, + @project) + end format.js { render nothing: true } end end @@ -59,7 +57,8 @@ class Projects::TeamMembersController < Projects::ApplicationController status = @project.team.import(giver) notice = status ? "Successfully imported" : "Import failed" - redirect_to project_team_index_path(project), notice: notice + redirect_to(namespace_project_team_index_path(project.namespace, project), + notice: notice) end protected diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 5b52640a4e1..b23010bf595 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -2,14 +2,17 @@ class Projects::TreeController < Projects::ApplicationController include ExtractsPath + before_filter :require_non_empty_project, except: [:new, :create] before_filter :assign_ref_vars before_filter :authorize_download_code! - before_filter :require_non_empty_project, except: [:new, :create] def show if tree.entries.empty? if @repository.blob_at(@commit.id, @path) - redirect_to project_blob_path(@project, File.join(@ref, @path)) and return + redirect_to( + namespace_project_blob_path(@project.namespace, @project, + File.join(@ref, @path)) + ) and return else return not_found! end diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb new file mode 100644 index 00000000000..9020e86c44e --- /dev/null +++ b/app/controllers/projects/uploads_controller.rb @@ -0,0 +1,35 @@ +class Projects::UploadsController < Projects::ApplicationController + layout 'project' + + before_filter :project + + def create + link_to_file = ::Projects::UploadService.new(project, params[:file]). + execute + + respond_to do |format| + if link_to_file + format.json do + render json: { link: link_to_file } + end + else + format.json do + render json: 'Invalid file.', status: :unprocessable_entity + end + end + end + end + + def show + uploader = FileUploader.new(project, params[:secret]) + + return redirect_to uploader.url unless uploader.file_storage? + + uploader.retrieve_from_store!(params[:filename]) + + return not_found! unless uploader.file.exists? + + disposition = uploader.image? ? 'inline' : 'attachment' + send_file uploader.file.path, disposition: disposition + end +end diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 0145207bf6f..643167947b9 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController before_filter :load_project_wiki def pages - @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(30) + @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE) end def show @@ -45,7 +45,7 @@ class Projects::WikisController < Projects::ApplicationController return render('empty') unless can?(current_user, :write_wiki, @project) if @page.update(content, format, message) - redirect_to [@project, @page], notice: 'Wiki was successfully updated.' + redirect_to [@project.namespace.becomes(Namespace), @project, @page], notice: 'Wiki was successfully updated.' else render 'edit' end @@ -55,7 +55,10 @@ class Projects::WikisController < Projects::ApplicationController @page = WikiPage.new(@project_wiki) if @page.create(wiki_params) - redirect_to project_wiki_path(@project, @page), notice: 'Wiki was successfully updated.' + redirect_to( + namespace_project_wiki_path(@project.namespace, @project, @page), + notice: 'Wiki was successfully updated.' + ) else render action: "edit" end @@ -65,7 +68,10 @@ class Projects::WikisController < Projects::ApplicationController @page = @project_wiki.find_page(params[:id]) unless @page - redirect_to(project_wiki_path(@project, :home), notice: "Page not found") + redirect_to( + namespace_project_wiki_path(@project.namespace, @project, :home), + notice: "Page not found" + ) end end @@ -73,7 +79,10 @@ class Projects::WikisController < Projects::ApplicationController @page = @project_wiki.find_page(params[:id]) @page.delete if @page - redirect_to project_wiki_path(@project, :home), notice: "Page was successfully deleted" + redirect_to( + namespace_project_wiki_path(@project.namespace, @project, :home), + notice: "Page was successfully deleted" + ) end def git_access @@ -88,7 +97,7 @@ class Projects::WikisController < Projects::ApplicationController @project_wiki.wiki rescue ProjectWiki::CouldNotCreateWikiError => ex flash[:notice] = "Could not create Wiki Repository at this time. Please try again later." - redirect_to @project + redirect_to project_path(@project) return false end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 462ab3d4749..0f28794b736 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,13 +1,15 @@ class ProjectsController < ApplicationController + prepend_before_filter :render_go_import, only: [:show] skip_before_filter :authenticate_user!, only: [:show] before_filter :project, except: [:new, :create] before_filter :repository, except: [:new, :create] # Authorize before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] + before_filter :set_title, only: [:new, :create] + before_filter :event_filter, only: :show layout 'navless', only: [:new, :create, :fork] - before_filter :set_title, only: [:new, :create] def new @project = Project.new @@ -21,7 +23,10 @@ class ProjectsController < ApplicationController @project = ::Projects::CreateService.new(current_user, project_params).execute if @project.saved? - redirect_to project_path(@project), notice: 'Project was successfully created.' + redirect_to( + project_path(@project), + notice: 'Project was successfully created.' + ) else render 'new' end @@ -33,7 +38,12 @@ class ProjectsController < ApplicationController respond_to do |format| if status flash[:notice] = 'Project was successfully updated.' - format.html { redirect_to edit_project_path(@project), notice: 'Project was successfully updated.' } + format.html do + redirect_to( + edit_project_path(@project), + notice: 'Project was successfully updated.' + ) + end format.js else format.html { render 'edit', layout: 'project_settings' } @@ -43,7 +53,8 @@ class ProjectsController < ApplicationController end def transfer - ::Projects::TransferService.new(project, current_user, project_params).execute + transfer_params = params.permit(:new_namespace_id) + ::Projects::TransferService.new(project, current_user, transfer_params).execute if @project.errors[:namespace_id].present? flash[:alert] = @project.errors[:namespace_id].first end @@ -51,14 +62,11 @@ class ProjectsController < ApplicationController def show if @project.import_in_progress? - redirect_to project_import_path(@project) + redirect_to namespace_project_import_path(@project.namespace, @project) return end limit = (params[:limit] || 20).to_i - @events = @project.events.recent - @events = event_filter.apply_filter(@events) - @events = @events.limit(limit).offset(params[:offset] || 0) @show_star = !(current_user && current_user.starred?(@project)) @@ -76,7 +84,12 @@ class ProjectsController < ApplicationController end end - format.json { pager_json('events/_events', @events.count) } + format.json do + @events = @project.events.recent + @events = event_filter.apply_filter(@events).with_associations + @events = @events.limit(limit).offset(params[:offset] || 0) + pager_json('events/_events', @events.count) + end end end @@ -90,9 +103,9 @@ class ProjectsController < ApplicationController flash[:alert] = 'Project deleted.' if request.referer.include?('/admin') - redirect_to admin_projects_path + redirect_to admin_namespaces_projects_path else - redirect_to projects_dashboard_path + redirect_to dashboard_path end end end @@ -121,7 +134,7 @@ class ProjectsController < ApplicationController @project.archive! respond_to do |format| - format.html { redirect_to @project } + format.html { redirect_to project_path(@project) } end end @@ -130,19 +143,7 @@ class ProjectsController < ApplicationController @project.unarchive! respond_to do |format| - format.html { redirect_to @project } - end - end - - def upload_image - link_to_image = ::Projects::ImageService.new(repository, params, root_url).execute - - respond_to do |format| - if link_to_image - format.json { render json: { link: link_to_image } } - else - format.json { render json: 'Invalid file.', status: :unprocessable_entity } - end + format.html { redirect_to project_path(@project) } end end @@ -158,15 +159,6 @@ class ProjectsController < ApplicationController private - def upload_path - base_dir = FileUploader.generate_dir - File.join(repository.path_with_namespace, base_dir) - end - - def accepted_images - %w(png jpg jpeg gif) - end - def set_title @title = 'New Project' end @@ -184,13 +176,23 @@ class ProjectsController < ApplicationController end def autocomplete_emojis - Rails.cache.fetch("autocomplete-emoji-#{Emoji::VERSION}") do - Emoji.names.map do |e| + Rails.cache.fetch("autocomplete-emoji-#{Gemojione::VERSION}") do + Emoji.emojis.map do |name, emoji| { - name: e, - path: view_context.image_url("emoji/#{e}.png") + name: name, + path: view_context.image_url("emoji/#{emoji["unicode"]}.png") } end end end + + def render_go_import + return unless params["go-get"] == "1" + + @namespace = params[:namespace_id] + @id = params[:project_id] || params[:id] + @id = @id.gsub(/\.git\Z/, "") + + render "go_import", layout: false + end end diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 1ed3bc388fb..ae501362dc2 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -16,7 +16,7 @@ class SnippetsController < ApplicationController layout :determine_layout def index - @snippets = SnippetsFinder.new.execute(current_user, filter: :all).page(params[:page]).per(20) + @snippets = SnippetsFinder.new.execute(current_user, filter: :all).page(params[:page]).per(PER_PAGE) end def user_index @@ -28,7 +28,7 @@ class SnippetsController < ApplicationController filter: :by_user, user: @user, scope: params[:scope] }). - page(params[:page]).per(20) + page(params[:page]).per(PER_PAGE) if @user == current_user render 'current_user_index' @@ -106,6 +106,7 @@ class SnippetsController < ApplicationController def set_title @title = 'Snippets' + @title_url = snippets_path end def snippet_params diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb new file mode 100644 index 00000000000..c5f3da54ea2 --- /dev/null +++ b/app/controllers/uploads_controller.rb @@ -0,0 +1,71 @@ +class UploadsController < ApplicationController + skip_before_filter :authenticate_user! + before_filter :find_model, :authorize_access! + + def show + uploader = @model.send(upload_mount) + + unless uploader.file_storage? + return redirect_to uploader.url + end + + unless uploader.file && uploader.file.exists? + return not_found! + end + + disposition = uploader.image? ? 'inline' : 'attachment' + send_file uploader.file.path, disposition: disposition + end + + private + + def find_model + unless upload_model && upload_mount + return not_found! + end + + @model = upload_model.find(params[:id]) + end + + def authorize_access! + authorized = + case @model + when Project + can?(current_user, :read_project, @model) + when Group + can?(current_user, :read_group, @model) + when Note + can?(current_user, :read_project, @model.project) + else + # No authentication required for user avatars. + true + end + + return if authorized + + if current_user + not_found! + else + authenticate_user! + end + end + + def upload_model + upload_models = { + user: User, + project: Project, + note: Note, + group: Group + } + + upload_models[params[:model].to_sym] + end + + def upload_mount + upload_mounts = %w(avatar attachment file) + + if upload_mounts.include?(params[:mounted_as]) + params[:mounted_as] + end + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 57d8ef09faf..8a13394dbac 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -4,21 +4,25 @@ class UsersController < ApplicationController layout :determine_layout def show - # Projects user can view - visible_projects = ProjectsFinder.new.execute(current_user) - authorized_projects_ids = visible_projects.pluck(:id) + @contributed_projects = Project. + where(id: authorized_projects_ids & @user.contributed_projects_ids). + in_group_namespace. + includes(:namespace). + reject(&:forked?) @projects = @user.personal_projects. - where(id: authorized_projects_ids) + where(id: authorized_projects_ids).includes(:namespace) # Collect only groups common for both users @groups = @user.groups & GroupsFinder.new.execute(current_user) # Get user activity feed for projects common for both users @events = @user.recent_events. - where(project_id: authorized_projects_ids).limit(30) + where(project_id: authorized_projects_ids). + with_associations.limit(30) @title = @user.name + @title_url = user_path(@user) respond_to do |format| format.html @@ -27,8 +31,8 @@ class UsersController < ApplicationController end def calendar - visible_projects = ProjectsFinder.new.execute(current_user) - calendar = Gitlab::CommitsCalendar.new(visible_projects, @user) + projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids) + calendar = Gitlab::CommitsCalendar.new(projects, @user) @timestamps = calendar.timestamps @starting_year = calendar.starting_year @starting_month = calendar.starting_month @@ -53,4 +57,10 @@ class UsersController < ApplicationController return authenticate_user! end end + + def authorized_projects_ids + # Projects user can view + @authorized_projects_ids ||= + ProjectsFinder.new.execute(current_user).pluck(:id) + end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index e1477510065..088a766ed3a 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -44,7 +44,7 @@ class IssuableFinder table_name = klass.table_name if project - if project.public? || (current_user && current_user.can?(:read_project, project)) + if Ability.abilities.allowed?(current_user, :read_project, project) project.send(table_name) else [] diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb index 32d7968924a..a79bd47d986 100644 --- a/app/finders/trending_projects_finder.rb +++ b/app/finders/trending_projects_finder.rb @@ -8,7 +8,7 @@ class TrendingProjectsFinder # for period of time - ex. month projects.joins(:notes).where('notes.created_at > ?', start_date). select("projects.*, count(notes.id) as ncount"). - group("projects.id").order("ncount DESC") + group("projects.id").reorder("ncount DESC") end private diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index 96e5d43a369..bb8d5683807 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -14,4 +14,8 @@ module AppearancesHelper def brand_text nil end + + def brand_header_logo + image_tag 'logo-white.png' + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e45f4650309..8ed6d59c20d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -51,11 +51,15 @@ module ApplicationHelper end def project_icon(project_id, options = {}) - project = Project.find_with_namespace(project_id) - if project.avatar.present? - image_tag project.avatar.url, options - elsif project.avatar_in_git - image_tag project_avatar_path(project), options + project = + if project_id.is_a?(Project) + project = project_id + else + Project.find_with_namespace(project_id) + end + + if project.avatar_url + image_tag project.avatar_url, options else # generated icon project_identicon(project, options) end @@ -82,15 +86,6 @@ module ApplicationHelper end end - def group_icon(group_path) - group = Group.find_by(path: group_path) - if group && group.avatar.present? - group.avatar.url - else - image_path('no_group_avatar.png') - end - end - def avatar_icon(user_email = '', size = nil) user = User.find_by(email: user_email) diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 04299316102..1ee086da997 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -3,6 +3,10 @@ module ApplicationSettingsHelper current_application_settings.gravatar_enabled? end + def twitter_sharing_enabled? + current_application_settings.twitter_sharing_enabled? + end + def signup_enabled? current_application_settings.signup_enabled? end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index e75eebd2da9..798d62b3a09 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -17,7 +17,7 @@ module BlobHelper end def no_highlight_files - %w(credits changelog copying copyright license authors) + %w(credits changelog news copying copyright license authors) end def edit_blob_link(project, ref, path, options = {}) @@ -36,8 +36,12 @@ module BlobHelper link_opts[:from_merge_request_id] = from_mr if from_mr cls = 'btn btn-small' if allowed_tree_edit?(project, ref) - link_to text, project_edit_blob_path(project, tree_join(ref, path), - link_opts), class: cls + link_to(text, + namespace_project_edit_blob_path(project.namespace, project, + tree_join(ref, path), + link_opts), + class: cls + ) else content_tag :span, text, class: cls + ' disabled' end + after.html_safe diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index b4ba14160ed..5aae697e2f0 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -37,7 +37,10 @@ module CommitsHelper # Add the root project link and the arrow icon crumbs = content_tag(:li) do - link_to(@project.path, project_commits_path(@project, @ref)) + link_to( + @project.path, + namespace_project_commits_path(@project.namespace, @project, @ref) + ) end if @path @@ -46,7 +49,14 @@ module CommitsHelper parts.each_with_index do |part, i| crumbs << content_tag(:li) do # The text is just the individual part, but the link needs all the parts before it - link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/'))) + link_to( + part, + namespace_project_commits_path( + @project.namespace, + @project, + tree_join(@ref, parts[0..i].join('/')) + ) + ) end end end @@ -63,7 +73,9 @@ module CommitsHelper # Returns the sorted alphabetically links to branches, separated by a comma def commit_branches_links(project, branches) branches.sort.map do |branch| - link_to(project_tree_path(project, branch)) do + link_to( + namespace_project_tree_path(project.namespace, project, branch) + ) do content_tag :span, class: 'label label-gray' do icon('code-fork') + ' ' + branch end @@ -75,7 +87,10 @@ module CommitsHelper def commit_tags_links(project, tags) sorted = VersionSorter.rsort(tags) sorted.map do |tag| - link_to(project_commits_path(project, project.repository.find_tag(tag).name)) do + link_to( + namespace_project_commits_path(project.namespace, project, + project.repository.find_tag(tag).name) + ) do content_tag :span, class: 'label label-gray' do icon('tag') + ' ' + tag end @@ -86,12 +101,26 @@ module CommitsHelper def link_to_browse_code(project, commit) if current_controller?(:projects, :commits) if @repo.blob_at(commit.id, @path) - return link_to "Browse File »", project_blob_path(project, tree_join(commit.id, @path)), class: "pull-right" + return link_to( + "Browse File »", + namespace_project_blob_path(project.namespace, project, + tree_join(commit.id, @path)), + class: "pull-right" + ) elsif @path.present? - return link_to "Browse Dir »", project_tree_path(project, tree_join(commit.id, @path)), class: "pull-right" + return link_to( + "Browse Dir »", + namespace_project_tree_path(project.namespace, project, + tree_join(commit.id, @path)), + class: "pull-right" + ) end end - link_to "Browse Code »", project_tree_path(project, commit), class: "pull-right" + link_to( + "Browse Code »", + namespace_project_tree_path(project.namespace, project, commit), + class: "pull-right" + ) end protected @@ -133,8 +162,11 @@ module CommitsHelper end def view_file_btn(commit_sha, diff, project) - link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)), - class: 'btn btn-small view-file js-view-file' do + link_to( + namespace_project_blob_path(project.namespace, project, + tree_join(commit_sha, diff.new_path)), + class: 'btn btn-small view-file js-view-file' + ) do raw('View file @') + content_tag(:span, commit_sha[0..6], class: 'commit-short-id') end diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb index dd2e713a54e..01847c6b807 100644 --- a/app/helpers/compare_helper.rb +++ b/app/helpers/compare_helper.rb @@ -10,6 +10,13 @@ module CompareHelper end def compare_mr_path - new_project_merge_request_path(@project, merge_request: { source_branch: params[:to], target_branch: params[:from] }) + new_namespace_project_merge_request_path( + @project.namespace, + @project, + merge_request: { + source_branch: params[:to], + target_branch: params[:from] + } + ) end end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index 4dae96644c8..c25b54eadc6 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -1,20 +1,4 @@ module DashboardHelper - def projects_dashboard_filter_path(options={}) - exist_opts = { - sort: params[:sort], - scope: params[:scope], - group: params[:group], - tag: params[:tag], - visibility_level: params[:visibility_level], - } - - options = exist_opts.merge(options) - - path = request.path - path << "?#{options.to_param}" - path - end - def assigned_issues_dashboard_path issues_dashboard_path(assignee_id: current_user.id) end diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index 92cc9c426b8..08476f8516e 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -1,3 +1,6 @@ +require 'html/pipeline' +require 'html/pipeline/gitlab' + module EmailsHelper # Google Actions @@ -39,4 +42,26 @@ module EmailsHelper lexer = Rugments::Lexers::Diff.new raw formatter.format(lexer.lex(diffcontent)) end + + def replace_image_links_with_base64(text, project) + # Used pipelines in GitLab: + # GitlabEmailImageFilter - replaces images that have been uploaded as attachments with inline images in emails. + # + # see https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters + filters = [ + HTML::Pipeline::Gitlab::GitlabEmailImageFilter + ] + + context = { + base_url: File.join(Gitlab.config.gitlab.url, project.path_with_namespace, 'uploads'), + upload_path: File.join(Rails.root, 'public', 'uploads', project.path_with_namespace), + } + + pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline + + result = pipeline.call(text, context) + text = result[:output].to_html(save_with: 0) + + text.html_safe + end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index d05f6df5f9f..779cebc0136 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -10,11 +10,15 @@ module EventsHelper end def event_action_name(event) - target = if event.target_type - event.target_type.titleize.downcase - else - 'project' - end + target = if event.target_type + if event.note? + event.note_target_type + else + event.target_type.titleize.downcase + end + else + 'project' + end [event.action_name, target].join(" ") end @@ -26,7 +30,7 @@ module EventsHelper end content_tag :li, class: "filter_icon #{active}" do - link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do + link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => 'Filter by ' + tooltip.downcase do icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip) end end @@ -42,36 +46,51 @@ module EventsHelper end def event_feed_title(event) - if event.issue? - "#{event.author_name} #{event.action_name} issue ##{event.target_iid}: #{event.issue_title} at #{event.project_name}" - elsif event.merge_request? - "#{event.author_name} #{event.action_name} MR ##{event.target_iid}: #{event.merge_request_title} at #{event.project_name}" - elsif event.push? - "#{event.author_name} #{event.push_action_name} #{event.ref_type} #{event.ref_name} at #{event.project_name}" - elsif event.membership_changed? - "#{event.author_name} #{event.action_name} #{event.project_name}" - elsif event.note? && event.note_commit? - "#{event.author_name} commented on #{event.note_target_type} #{event.note_short_commit_id} at #{event.project_name}" - elsif event.note? - "#{event.author_name} commented on #{event.note_target_type} ##{truncate event.note_target_iid} at #{event.project_name}" - else - "" + words = [] + words << event.author_name + words << event_action_name(event) + + if event.push? + words << event.ref_type + words << event.ref_name + words << "at" + elsif event.commented? + if event.note_commit? + words << event.note_short_commit_id + else + words << "##{truncate event.note_target_iid}" + end + words << "at" + elsif event.target + words << "##{event.target_iid}:" + words << event.target.title if event.target.respond_to?(:title) + words << "at" end + + words << event.project_name + + words.join(" ") end def event_feed_url(event) if event.issue? - project_issue_url(event.project, event.issue) + namespace_project_issue_url(event.project.namespace, event.project, + event.issue) elsif event.merge_request? - project_merge_request_url(event.project, event.merge_request) + namespace_project_merge_request_url(event.project.namespace, + event.project, event.merge_request) elsif event.note? && event.note_commit? - project_commit_url(event.project, event.note_target) + namespace_project_commit_url(event.project.namespace, event.project, + event.note_target) elsif event.note? if event.note_target if event.note_commit? - project_commit_path(event.project, event.note_commit_id, anchor: dom_id(event.target)) + namespace_project_commit_path(event.project.namespace, event.project, + event.note_commit_id, + anchor: dom_id(event.target)) elsif event.note_project_snippet? - project_snippet_path(event.project, event.note_target) + namespace_project_snippet_path(event.project.namespace, + event.project, event.note_target) else event_note_target_path(event) end @@ -79,12 +98,16 @@ module EventsHelper elsif event.push? if event.push_with_commits? if event.commits_count > 1 - project_compare_url(event.project, from: event.commit_from, to: event.commit_to) + namespace_project_compare_url(event.project.namespace, event.project, + from: event.commit_from, to: + event.commit_to) else - project_commit_url(event.project, id: event.commit_to) + namespace_project_commit_url(event.project.namespace, event.project, + id: event.commit_to) end else - project_commits_url(event.project, event.ref_name) + namespace_project_commits_url(event.project.namespace, event.project, + event.ref_name) end end end @@ -96,8 +119,6 @@ module EventsHelper render "events/event_push", event: event elsif event.merge_request? render "events/event_merge_request", merge_request: event.merge_request - elsif event.push? - render "events/event_push", event: event elsif event.note? render "events/event_note", note: event.note end @@ -105,20 +126,30 @@ module EventsHelper def event_note_target_path(event) if event.note? && event.note_commit? - project_commit_path(event.project, event.note_target) + namespace_project_commit_path(event.project.namespace, event.project, + event.note_target) else - polymorphic_path([event.project, event.note_target], anchor: dom_id(event.target)) + polymorphic_path([event.project.namespace.becomes(Namespace), + event.project, event.note_target], + anchor: dom_id(event.target)) end end def event_note_title_html(event) if event.note_target if event.note_commit? - link_to project_commit_path(event.project, event.note_commit_id, anchor: dom_id(event.target)), class: "commit_short_id" do + link_to( + namespace_project_commit_path(event.project.namespace, event.project, + event.note_commit_id, + anchor: dom_id(event.target)), + class: "commit_short_id" + ) do "#{event.note_target_type} #{event.note_short_commit_id}" end elsif event.note_project_snippet? - link_to(project_snippet_path(event.project, event.note_target)) do + link_to(namespace_project_snippet_path(event.project.namespace, + event.project, + event.note_target)) do "#{event.note_target_type} ##{truncate event.note_target_id}" end else @@ -135,7 +166,7 @@ module EventsHelper def event_note(text) text = first_line_in_markdown(text, 150) - sanitize(text, tags: %w(a img b pre code p)) + sanitize(text, tags: %w(a img b pre code p span)) end def event_commit_title(message) diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb new file mode 100644 index 00000000000..7616fe6bad8 --- /dev/null +++ b/app/helpers/explore_helper.rb @@ -0,0 +1,17 @@ +module ExploreHelper + def explore_projects_filter_path(options={}) + exist_opts = { + sort: params[:sort], + scope: params[:scope], + group: params[:group], + tag: params[:tag], + visibility_level: params[:visibility_level], + } + + options = exist_opts.merge(options) + + path = request.path + path << "?#{options.to_param}" + path + end +end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 800cacdc2c2..f8e104b0827 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -31,7 +31,9 @@ module GitlabMarkdownHelper def markdown(text, options={}) unless (@markdown and options == @options) @options = options - gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, { + gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, + user_color_scheme_class, + { # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- filter_html: true, with_toc_data: true, @@ -110,7 +112,7 @@ module GitlabMarkdownHelper end def link_to_ignore?(link) - if link =~ /\#\w+/ + if link =~ /\A\#\w+/ # ignore anchors like <a href="#my-header"> true else @@ -119,13 +121,14 @@ module GitlabMarkdownHelper end def ignored_protocols - ["http://","https://", "ftp://", "mailto:"] + ["http://","https://", "ftp://", "mailto:", "smb://"] end - def rebuild_path(path) - path.gsub!(/(#.*)/, "") + def rebuild_path(file_path) + file_path = file_path.dup + file_path.gsub!(/(#.*)/, "") id = $1 || "" - file_path = relative_file_path(path) + file_path = relative_file_path(file_path) file_path = sanitize_slashes(file_path) [ diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb new file mode 100644 index 00000000000..8518a47a3a0 --- /dev/null +++ b/app/helpers/gitlab_routing_helper.rb @@ -0,0 +1,51 @@ +# Shorter routing method for project and project items +# Since update to rails 4.1.9 we are now allowed to use `/` in project routing +# so we use nested routing for project resources which include project and +# project namespace. To avoid writing long methods every time we define shortcuts for +# some of routing. +# +# For example instead of this: +# +# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request) +# +# We can simply use shortcut: +# +# merge_request_path(merge_request) +# +module GitlabRoutingHelper + def project_path(project, *args) + namespace_project_path(project.namespace, project, *args) + end + + def edit_project_path(project, *args) + edit_namespace_project_path(project.namespace, project, *args) + end + + def issue_path(entity, *args) + namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) + end + + def merge_request_path(entity, *args) + namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args) + end + + def project_url(project, *args) + namespace_project_url(project.namespace, project, *args) + end + + def edit_project_url(project, *args) + edit_namespace_project_url(project.namespace, project, *args) + end + + def issue_url(entity, *args) + namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args) + end + + def merge_request_url(entity, *args) + namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args) + end + + def snippet_url(entity, *args) + namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) + end +end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 03fd461a462..2d0d0b494f6 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -40,4 +40,16 @@ module GroupsHelper false end end + + def group_icon(group) + if group.is_a?(String) + group = Group.find_by(path: group) + end + + if group && group.avatar.present? + group.avatar.url + else + image_path('no_group_avatar.png') + end + end end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index e1c1078344e..15c5dcb6a25 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -93,8 +93,10 @@ module IssuesHelper def issue_to_atom(xml, issue) xml.entry do - xml.id project_issue_url(issue.project, issue) - xml.link href: project_issue_url(issue.project, issue) + xml.id namespace_project_issue_url(issue.project.namespace, + issue.project, issue) + xml.link href: namespace_project_issue_url(issue.project.namespace, + issue.project, issue) xml.title truncate(issue.title, length: 80) xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index add0fef512e..49063491abf 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -14,14 +14,27 @@ module LabelsHelper def suggested_colors [ - '#D9534F', - '#F0AD4E', + '#0033CC', '#428BCA', + '#44AD8E', + '#A8D695', '#5CB85C', + '#69D100', + '#004E00', '#34495E', '#7F8C8D', + '#A295D6', + '#5843AD', '#8E44AD', - '#FFECDB' + '#FFECDB', + '#AD4363', + '#D10069', + '#CC0033', + '#FF0000', + '#D9534F', + '#D1D100', + '#F0AD4E', + '#AD8D43' ] end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 4c640d4fc5f..3b1589da57f 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,14 +1,16 @@ module MergeRequestsHelper def new_mr_path_from_push_event(event) target_project = event.project.forked_from_project || event.project - new_project_merge_request_path( + new_namespace_project_merge_request_path( + event.project.namespace, event.project, new_mr_from_push_event(event, target_project) ) end def new_mr_path_for_fork_from_push_event(event) - new_project_merge_request_path( + new_namespace_project_merge_request_path( + event.project.namespace, event.project, new_mr_from_push_event(event, event.project.forked_from_project) ) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 6847123d2d4..59fdc0d49cc 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -1,9 +1,22 @@ module MilestonesHelper def milestones_filter_path(opts = {}) if @project - project_milestones_path(@project, opts) + namespace_project_milestones_path(@project.namespace, @project, opts) elsif @group group_milestones_path(@group, opts) + else + dashboard_milestones_path(opts) + end + end + + def milestone_progress_bar(milestone) + options = { + class: 'progress-bar progress-bar-success', + style: "width: #{milestone.percent_complete}%;" + } + + content_tag :div, class: 'progress' do + content_tag :div, nil, options end end end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 2bcfde62830..b3132a1f3ba 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -28,7 +28,7 @@ module NamespacesHelper def namespace_icon(namespace, size = 40) if namespace.kind_of?(Group) - group_icon(namespace.path) + group_icon(namespace) else avatar_icon(namespace.owner.email, size) end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 8edcb8e6a80..ab44fa6ee43 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -4,14 +4,18 @@ module NotesHelper (@noteable.class.name == note.noteable_type && !note.for_diff_line?) end - def note_target_fields - hidden_field_tag(:target_type, @target_type) + - hidden_field_tag(:target_id, @target_id) + def note_target_fields(note) + hidden_field_tag(:target_type, note.noteable.class.name.underscore) + + hidden_field_tag(:target_id, note.noteable.id) end def link_to_commit_diff_line_note(note) if note.for_commit_diff_line? - link_to "#{note.diff_file_name}:L#{note.diff_new_line}", project_commit_path(@project, note.noteable, anchor: note.line_code) + link_to( + "#{note.diff_file_name}:L#{note.diff_new_line}", + namespace_project_commit_path(@project.namespace, @project, + note.noteable, anchor: note.line_code) + ) end end diff --git a/app/helpers/oauth_helper.rb b/app/helpers/oauth_helper.rb index c7bc9307a58..1a0ad17b607 100644 --- a/app/helpers/oauth_helper.rb +++ b/app/helpers/oauth_helper.rb @@ -4,7 +4,7 @@ module OauthHelper end def default_providers - [:twitter, :github, :gitlab, :google_oauth2, :ldap] + [:twitter, :github, :gitlab, :bitbucket, :google_oauth2, :ldap] end def enabled_oauth_providers @@ -13,11 +13,13 @@ module OauthHelper def enabled_social_providers enabled_oauth_providers.select do |name| - [:twitter, :gitlab, :github, :google_oauth2].include?(name.to_sym) + [:twitter, :gitlab, :github, :bitbucket, :google_oauth2].include?(name.to_sym) end end def additional_providers enabled_oauth_providers.reject{|provider| provider.to_s.starts_with?('ldap')} end + + extend self end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 36463892ebf..2225b110651 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -4,7 +4,7 @@ module ProjectsHelper end def link_to_project(project) - link_to project do + link_to [project.namespace.becomes(Namespace), project] do title = content_tag(:span, project.name, class: 'project-name') if project.namespace @@ -42,12 +42,20 @@ module ProjectsHelper def project_title(project) if project.group content_tag :span do - link_to(simple_sanitize(project.group.name), group_path(project.group)) + ' / ' + link_to(simple_sanitize(project.name), project_path(project)) + link_to( + simple_sanitize(project.group.name), group_path(project.group) + ) + ' / ' + + link_to(simple_sanitize(project.name), + project_path(project)) end else owner = project.namespace.owner content_tag :span do - link_to(simple_sanitize(owner.name), user_path(owner)) + ' / ' + link_to(simple_sanitize(project.name), project_path(project)) + link_to( + simple_sanitize(owner.name), user_path(owner) + ) + ' / ' + + link_to(simple_sanitize(project.name), + project_path(project)) end end end @@ -100,7 +108,10 @@ module ProjectsHelper content_tag 'span', class: starred ? 'turn-on' : 'turn-off' do - link_to toggle_star_project_path(@project), link_opts do + link_to( + toggle_star_namespace_project_path(@project.namespace, @project), + link_opts + ) do toggle_html + ' ' + count_html end end @@ -222,7 +233,12 @@ module ProjectsHelper def contribution_guide_url(project) if project && project.repository.contribution_guide - project_blob_path(project, tree_join(project.default_branch, project.repository.contribution_guide.name)) + namespace_project_blob_path( + project.namespace, + project, + tree_join(project.default_branch, + project.repository.contribution_guide.name) + ) end end @@ -236,7 +252,7 @@ module ProjectsHelper def project_wiki_path_with_version(proj, page, version, is_newest) url_params = is_newest ? {} : { version_id: version } - project_wiki_path(proj, page, url_params) + namespace_project_wiki_path(proj.namespace, proj, page, url_params) end def project_status_css_class(status) @@ -250,11 +266,13 @@ module ProjectsHelper end end - def github_import_enabled? - enabled_oauth_providers.include?(:github) - end + def service_field_value(type, value) + return value unless type == 'password' - def gitlab_import_enabled? - enabled_oauth_providers.include?(:gitlab) + if value.present? + "***********" + else + nil + end end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 65b9408cfa1..cb829037697 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -52,16 +52,16 @@ module SearchHelper ref = @ref || @project.repository.root_ref [ - { label: "#{prefix} - Files", url: project_tree_path(@project, ref) }, - { label: "#{prefix} - Commits", url: project_commits_path(@project, ref) }, - { label: "#{prefix} - Network", url: project_network_path(@project, ref) }, - { label: "#{prefix} - Graph", url: project_graph_path(@project, ref) }, - { label: "#{prefix} - Issues", url: project_issues_path(@project) }, - { label: "#{prefix} - Merge Requests", url: project_merge_requests_path(@project) }, - { label: "#{prefix} - Milestones", url: project_milestones_path(@project) }, - { label: "#{prefix} - Snippets", url: project_snippets_path(@project) }, - { label: "#{prefix} - Team", url: project_team_index_path(@project) }, - { label: "#{prefix} - Wiki", url: project_wikis_path(@project) }, + { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) }, + { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) }, + { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, + { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, + { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, + { label: "#{prefix} - Team", url: namespace_project_team_index_path(@project.namespace, @project) }, + { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, ] else [] @@ -84,7 +84,7 @@ module SearchHelper sorted_by_stars.non_archived.limit(limit).map do |p| { label: "project: #{search_result_sanitize(p.name_with_namespace)}", - url: project_path(p) + url: namespace_project_path(p.namespace, p) } end end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index b0abc2cae33..906cb12cd48 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -11,7 +11,8 @@ module SnippetsHelper def reliable_snippet_path(snippet) if snippet.project_id? - project_snippet_path(snippet.project, snippet) + namespace_project_snippet_path(snippet.project.namespace, + snippet.project, snippet) else snippet_path(snippet) end diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index 841e7fd17f6..525266fb3b5 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -5,19 +5,22 @@ module SubmoduleHelper def submodule_links(submodule_item, ref = nil) url = @repository.submodule_url_for(ref, submodule_item.path) - return url, nil unless url =~ /([^\/:]+\/[^\/]+\.git)\Z/ + return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/ - project = $1 + namespace = $1 + project = $2 project.chomp!('.git') - if self_url?(url, project) - return project_path(project), project_tree_path(project, submodule_item.id) + if self_url?(url, namespace, project) + return namespace_project_path(namespace, project), + namespace_project_tree_path(namespace, project, + submodule_item.id) elsif relative_self_url?(url) relative_self_links(url, submodule_item.id) elsif github_dot_com_url?(url) - standard_links('github.com', project, submodule_item.id) + standard_links('github.com', namespace, project, submodule_item.id) elsif gitlab_dot_com_url?(url) - standard_links('gitlab.com', project, submodule_item.id) + standard_links('gitlab.com', namespace, project, submodule_item.id) else return url, nil end @@ -33,9 +36,10 @@ module SubmoduleHelper url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/ end - def self_url?(url, project) - return true if url == [ Gitlab.config.gitlab.url, '/', project, '.git' ].join('') - url == gitlab_shell.url_to_repo(project) + def self_url?(url, namespace, project) + return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/', + project, '.git' ].join('') + url == gitlab_shell.url_to_repo([namespace, '/', project].join('')) end def relative_self_url?(url) @@ -43,8 +47,8 @@ module SubmoduleHelper url =~ /^((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\Z/ || url =~ /^((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\Z/ end - def standard_links(host, project, commit) - base = [ 'https://', host, '/', project ].join('') + def standard_links(host, namespace, project, commit) + base = [ 'https://', host, '/', namespace, '/', project ].join('') return base, [ base, '/tree/', commit ].join('') end @@ -54,6 +58,7 @@ module SubmoduleHelper else base = [ @project.group.path, '/', url[/([^\/]*)\.git/, 1] ].join('') end - return project_path(base), project_tree_path(base, commit) + return namespace_project_path(base.namespace, base), + namespace_project_tree_path(base.namespace, base, commit) end end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 2142db29925..7a401a274d3 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -97,7 +97,8 @@ module TabHelper def branches_tab_class if current_controller?(:protected_branches) || current_controller?(:branches) || - current_page?(project_repository_path(@project)) + current_page?(namespace_project_repository_path(@project.namespace, + @project)) 'active' end end diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index e5346235963..687bac3aa31 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -3,7 +3,7 @@ module Emails def new_issue_email(recipient_id, issue_id) @issue = Issue.find(issue_id) @project = @issue.project - @target_url = project_issue_url(@project, @issue) + @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) mail_new_thread(@issue, from: sender(@issue.author_id), to: recipient(recipient_id), @@ -14,7 +14,7 @@ module Emails @issue = Issue.find(issue_id) @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @project = @issue.project - @target_url = project_issue_url(@project, @issue) + @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) mail_answer_thread(@issue, from: sender(updated_by_user_id), to: recipient(recipient_id), @@ -25,7 +25,7 @@ module Emails @issue = Issue.find issue_id @project = @issue.project @updated_by = User.find updated_by_user_id - @target_url = project_issue_url(@project, @issue) + @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) mail_answer_thread(@issue, from: sender(updated_by_user_id), to: recipient(recipient_id), @@ -37,7 +37,7 @@ module Emails @issue_status = status @project = @issue.project @updated_by = User.find updated_by_user_id - @target_url = project_issue_url(@project, @issue) + @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) mail_answer_thread(@issue, from: sender(updated_by_user_id), to: recipient(recipient_id), diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 7f6c855c301..512a8f7ea6b 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -3,7 +3,9 @@ module Emails def new_merge_request_email(recipient_id, merge_request_id) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project - @target_url = project_merge_request_url(@project, @merge_request) + @target_url = namespace_project_merge_request_url(@project.namespace, + @project, + @merge_request) mail_new_thread(@merge_request, from: sender(@merge_request.author_id), to: recipient(recipient_id), @@ -14,7 +16,9 @@ module Emails @merge_request = MergeRequest.find(merge_request_id) @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @project = @merge_request.project - @target_url = project_merge_request_url(@project, @merge_request) + @target_url = namespace_project_merge_request_url(@project.namespace, + @project, + @merge_request) mail_answer_thread(@merge_request, from: sender(updated_by_user_id), to: recipient(recipient_id), @@ -25,7 +29,9 @@ module Emails @merge_request = MergeRequest.find(merge_request_id) @updated_by = User.find updated_by_user_id @project = @merge_request.project - @target_url = project_merge_request_url(@project, @merge_request) + @target_url = namespace_project_merge_request_url(@project.namespace, + @project, + @merge_request) mail_answer_thread(@merge_request, from: sender(updated_by_user_id), to: recipient(recipient_id), @@ -35,7 +41,9 @@ module Emails def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) @merge_request = MergeRequest.find(merge_request_id) @project = @merge_request.project - @target_url = project_merge_request_url(@project, @merge_request) + @target_url = namespace_project_merge_request_url(@project.namespace, + @project, + @merge_request) mail_answer_thread(@merge_request, from: sender(updated_by_user_id), to: recipient(recipient_id), @@ -47,7 +55,9 @@ module Emails @mr_status = status @project = @merge_request.project @updated_by = User.find updated_by_user_id - @target_url = project_merge_request_url(@project, @merge_request) + @target_url = namespace_project_merge_request_url(@project.namespace, + @project, + @merge_request) set_reference("merge_request_#{merge_request_id}") mail_answer_thread(@merge_request, from: sender(updated_by_user_id), diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index ef9af726a6c..ff251209e01 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -4,7 +4,9 @@ module Emails @note = Note.find(note_id) @commit = @note.noteable @project = @note.project - @target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}") + @target_url = namespace_project_commit_url(@project.namespace, @project, + @commit, anchor: + "note_#{@note.id}") mail_answer_thread(@commit, from: sender(@note.author_id), to: recipient(recipient_id), @@ -15,7 +17,9 @@ module Emails @note = Note.find(note_id) @issue = @note.noteable @project = @note.project - @target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}") + @target_url = namespace_project_issue_url(@project.namespace, @project, + @issue, anchor: + "note_#{@note.id}") mail_answer_thread(@issue, from: sender(@note.author_id), to: recipient(recipient_id), @@ -26,7 +30,10 @@ module Emails @note = Note.find(note_id) @merge_request = @note.noteable @project = @note.project - @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") + @target_url = namespace_project_merge_request_url(@project.namespace, + @project, + @merge_request, anchor: + "note_#{@note.id}") mail_answer_thread(@merge_request, from: sender(@note.author_id), to: recipient(recipient_id), diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index dc2ebc969c1..b55129de292 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -3,7 +3,7 @@ module Emails def project_access_granted_email(user_project_id) @project_member = ProjectMember.find user_project_id @project = @project_member.project - @target_url = project_url(@project) + @target_url = namespace_project_url(@project.namespace, @project) mail(to: @project_member.user.email, subject: subject("Access to project was granted")) end @@ -11,29 +11,43 @@ module Emails def project_was_moved_email(project_id, user_id) @user = User.find user_id @project = Project.find project_id - @target_url = project_url(@project) + @target_url = namespace_project_url(@project.namespace, @project) mail(to: @user.notification_email, subject: subject("Project was moved")) end - def repository_push_email(project_id, recipient, author_id, branch, compare) + def repository_push_email(project_id, recipient, author_id, branch, compare, reverse_compare = false, send_from_committer_email = false, disable_diffs = false) @project = Project.find(project_id) @author = User.find(author_id) + @reverse_compare = reverse_compare @compare = compare @commits = Commit.decorate(compare.commits) @diffs = compare.diffs - @branch = branch + @branch = Gitlab::Git.ref_name(branch) + @disable_diffs = disable_diffs + + @subject = "[#{@project.path_with_namespace}][#{@branch}] " + if @commits.length > 1 - @target_url = project_compare_url(@project, from: @commits.first, to: @commits.last) - @subject = "#{@commits.length} new commits pushed to repository" + @target_url = namespace_project_compare_url(@project.namespace, + @project, + from: Commit.new(@compare.base), + to: Commit.new(@compare.head)) + @subject << "Deleted " if @reverse_compare + @subject << "#{@commits.length} commits: #{@commits.first.title}" else - @target_url = project_commit_url(@project, @commits.first) - @subject = @commits.first.title + @target_url = namespace_project_commit_url(@project.namespace, + @project, @commits.first) + + @subject << "Deleted 1 commit: " if @reverse_compare + @subject << @commits.first.title end - mail(from: sender(author_id), + @disable_footer = true + + mail(from: sender(author_id, send_from_committer_email), to: recipient, - subject: subject(@subject)) + subject: @subject) end end end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 46ead62f75f..ee27879cf40 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -34,21 +34,41 @@ class Notify < ActionMailer::Base ) end + # Splits "gitlab.corp.company.com" up into "gitlab.corp.company.com", + # "corp.company.com" and "company.com". + # Respects set tld length so "company.co.uk" won't match "somethingelse.uk" + def self.allowed_email_domains + domain_parts = Gitlab.config.gitlab.host.split(".") + allowed_domains = [] + begin + allowed_domains << domain_parts.join(".") + domain_parts.shift + end while domain_parts.length > ActionDispatch::Http::URL.tld_length + + allowed_domains + end + private # The default email address to send emails from def default_sender_address address = Mail::Address.new(Gitlab.config.gitlab.email_from) - address.display_name = "GitLab" + address.display_name = Gitlab.config.gitlab.email_display_name address end # Return an email address that displays the name of the sender. # Only the displayed name changes; the actual email address is always the same. - def sender(sender_id) + def sender(sender_id, send_from_user_email = false) if sender = User.find(sender_id) address = default_sender_address address.display_name = sender.name + + sender_domain = sender.email.split("@").last + if send_from_user_email && self.class.allowed_email_domains.include?(sender_domain) + address.address = sender.email + end + address.format end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 6d4e220b16c..588668b3d1e 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -2,16 +2,17 @@ # # Table name: application_settings # -# id :integer not null, primary key -# default_projects_limit :integer -# default_branch_protection :integer -# signup_enabled :boolean -# signin_enabled :boolean -# gravatar_enabled :boolean -# sign_in_text :text -# created_at :datetime -# updated_at :datetime -# home_page_url :string(255) +# id :integer not null, primary key +# default_projects_limit :integer +# signup_enabled :boolean +# signin_enabled :boolean +# gravatar_enabled :boolean +# sign_in_text :text +# created_at :datetime +# updated_at :datetime +# home_page_url :string(255) +# default_branch_protection :integer default(2) +# twitter_sharing_enabled :boolean default(TRUE) # class ApplicationSetting < ActiveRecord::Base @@ -30,6 +31,7 @@ class ApplicationSetting < ActiveRecord::Base default_branch_protection: Settings.gitlab['default_branch_protection'], signup_enabled: Settings.gitlab['signup_enabled'], signin_enabled: Settings.gitlab['signin_enabled'], + twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'], gravatar_enabled: Settings.gravatar['enabled'], sign_in_text: Settings.extra['sign_in_text'], ) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index d640728519a..74900d4675d 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -67,9 +67,10 @@ module Mentionable return [] if text.blank? ext = Gitlab::ReferenceExtractor.new ext.analyze(text, p) - (ext.issues_for + - ext.merge_requests_for + - ext.commits_for).uniq - [local_reference] + + (ext.issues_for(p) + + ext.merge_requests_for(p) + + ext.commits_for(p)).uniq - [local_reference] end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. @@ -98,5 +99,4 @@ module Mentionable preexisting = references(p, original) create_cross_references!(p, a, preexisting) end - end diff --git a/app/models/event.rb b/app/models/event.rb index 9a42d380f87..8d20d7ef252 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -47,31 +47,9 @@ class Event < ActiveRecord::Base scope :recent, -> { order("created_at DESC") } scope :code_push, -> { where(action: PUSHED) } scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } + scope :with_associations, -> { includes(project: :namespace) } class << self - def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads') - commit = project.repository.commit(ref.target) - - if action.to_s == 'add' - before = '00000000' - after = commit.id - else - before = commit.id - after = '00000000' - end - - Event.create( - project: project, - action: Event::PUSHED, - data: { - ref: "#{prefix}/#{ref.name}", - before: before, - after: after - }, - author_id: user.id - ) - end - def reset_event_cache_for(target) Event.where(target_id: target.id, target_type: target.class.to_s). order('id DESC').limit(100). @@ -84,6 +62,8 @@ class Event < ActiveRecord::Base true elsif membership_changed? true + elsif created_project? + true else (issue? || merge_request? || note? || milestone?) && target end @@ -98,25 +78,51 @@ class Event < ActiveRecord::Base end def target_title - if target && target.respond_to?(:title) - target.title - end + target.title if target && target.respond_to?(:title) + end + + def created? + action == CREATED end def push? - action == self.class::PUSHED && valid_push? + action == PUSHED && valid_push? end def merged? - action == self.class::MERGED + action == MERGED end def closed? - action == self.class::CLOSED + action == CLOSED end def reopened? - action == self.class::REOPENED + action == REOPENED + end + + def joined? + action == JOINED + end + + def left? + action == LEFT + end + + def commented? + action == COMMENTED + end + + def membership_changed? + joined? || left? + end + + def created_project? + created? && !target + end + + def created_target? + created? && target end def milestone? @@ -135,32 +141,32 @@ class Event < ActiveRecord::Base target_type == "MergeRequest" end - def joined? - action == JOINED - end - - def left? - action == LEFT - end - - def membership_changed? - joined? || left? + def milestone + target if milestone? end def issue - target if target_type == "Issue" + target if issue? end def merge_request - target if target_type == "MergeRequest" + target if merge_request? end def note - target if target_type == "Note" + target if note? end def action_name - if closed? + if push? + if new_ref? + "pushed new" + elsif rm_ref? + "deleted" + else + "pushed to" + end + elsif closed? "closed" elsif merged? "accepted" @@ -168,6 +174,10 @@ class Event < ActiveRecord::Base 'joined' elsif left? 'left' + elsif commented? + "commented on" + elsif created_project? + "created" else "opened" end @@ -180,19 +190,19 @@ class Event < ActiveRecord::Base end def tag? - data[:ref]["refs/tags"] + Gitlab::Git.tag_ref?(data[:ref]) end def branch? - data[:ref]["refs/heads"] + Gitlab::Git.branch_ref?(data[:ref]) end def new_ref? - commit_from =~ /^00000/ + Gitlab::Git.blank_ref?(commit_from) end def rm_ref? - commit_to =~ /^00000/ + Gitlab::Git.blank_ref?(commit_to) end def md_ref? @@ -216,11 +226,11 @@ class Event < ActiveRecord::Base end def branch_name - @branch_name ||= data[:ref].gsub("refs/heads/", "") + @branch_name ||= Gitlab::Git.ref_name(data[:ref]) end def tag_name - @tag_name ||= data[:ref].gsub("refs/tags/", "") + @tag_name ||= Gitlab::Git.ref_name(data[:ref]) end # Max 20 commits from push DESC @@ -236,16 +246,6 @@ class Event < ActiveRecord::Base tag? ? "tag" : "branch" end - def push_action_name - if new_ref? - "pushed new" - elsif rm_ref? - "deleted" - else - "pushed to" - end - end - def push_with_commits? md_ref? && commits.any? && commit_from && commit_to end diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb new file mode 100644 index 00000000000..50efcb32f1b --- /dev/null +++ b/app/models/external_issue.rb @@ -0,0 +1,25 @@ +class ExternalIssue + def initialize(issue_identifier, project) + @issue_identifier, @project = issue_identifier, project + end + + def to_s + @issue_identifier.to_s + end + + def id + @issue_identifier.to_s + end + + def iid + @issue_identifier.to_s + end + + def ==(other) + other.is_a?(self.class) && (to_s == other.to_s) + end + + def project + @project + end +end diff --git a/app/models/group.rb b/app/models/group.rb index d6ec0be6081..da9621a2a1a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -23,7 +23,7 @@ class Group < Namespace validate :avatar_type, if: ->(user) { user.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } - mount_uploader :avatar, AttachmentUploader + mount_uploader :avatar, AvatarUploader after_create :post_create_hook after_destroy :post_destroy_hook diff --git a/app/models/identity.rb b/app/models/identity.rb index b2c3792d1ce..440fcd0d052 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -6,6 +6,8 @@ # extern_uid :string(255) # provider :string(255) # user_id :integer +# created_at :datetime +# updated_at :datetime # class Identity < ActiveRecord::Base diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index 30c09f768d7..e4791d0f0aa 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -114,13 +114,11 @@ class ProjectMember < Member end def post_create_hook - Event.create( - project_id: self.project.id, - action: Event::JOINED, - author_id: self.user.id - ) - - notification_service.new_team_member(self) unless owner? + unless owner? + event_service.join_project(self.project, self.user) + notification_service.new_team_member(self) + end + system_hook_service.execute_hooks_for(self, :create) end @@ -129,15 +127,14 @@ class ProjectMember < Member end def post_destroy_hook - Event.create( - project_id: self.project.id, - action: Event::LEFT, - author_id: self.user.id - ) - + event_service.leave_project(self.project, self.user) system_hook_service.execute_hooks_for(self, :destroy) end + def event_service + EventCreateService.new + end + def notification_service NotificationService.new end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index ba0b2b71cf9..35280889a86 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -44,6 +44,15 @@ class Namespace < ActiveRecord::Base scope :root, -> { where('type IS NULL') } + def self.by_path(path) + where('lower(path) = :value', value: path.downcase).first + end + + # Case insensetive search for namespace by path or name + def self.find_by_path_or_name(path) + find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase) + end + def self.search(query) where("name LIKE :query OR path LIKE :query", query: "%#{query}%") end diff --git a/app/models/note.rb b/app/models/note.rb index ccd9783e7d4..9ca3e4d7e97 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -151,18 +151,41 @@ class Note < ActiveRecord::Base ) end - def create_new_commits_note(noteable, project, author, commits) - commits_text = ActionController::Base.helpers.pluralize(commits.size, 'new commit') + def create_new_commits_note(merge_request, project, author, new_commits, existing_commits = []) + total_count = new_commits.length + existing_commits.length + commits_text = ActionController::Base.helpers.pluralize(total_count, 'commit') body = "Added #{commits_text}:\n\n" - commits.each do |commit| + if existing_commits.length > 0 + commit_ids = + if existing_commits.length == 1 + existing_commits.first.short_id + else + "#{existing_commits.first.short_id}..#{existing_commits.last.short_id}" + end + + commits_text = ActionController::Base.helpers.pluralize(existing_commits.length, 'commit') + + branch = + if merge_request.for_fork? + "#{merge_request.target_project_namespace}:#{merge_request.target_branch}" + else + merge_request.target_branch + end + + message = "* #{commit_ids} - _#{commits_text} from branch `#{branch}`_" + body << message + body << "\n" + end + + new_commits.each do |commit| message = "* #{commit.short_id} - #{commit.title}" body << message body << "\n" end create( - noteable: noteable, + noteable: merge_request, project: project, author: author, note: body, @@ -308,6 +331,10 @@ class Note < ActiveRecord::Base end end + def hook_attrs + attributes + end + def set_diff # First lets find notes with same diff # before iterating over all mr diffs @@ -409,19 +436,19 @@ class Note < ActiveRecord::Base prev_lines = [] diff_lines.each do |line| - if generate_line_code(line) != self.line_code - if line.type == "match" - prev_lines.clear - prev_match_line = line - else - prev_lines.push(line) - prev_lines.shift if prev_lines.length >= max_number_of_lines - end + if line.type == "match" + prev_lines.clear + prev_match_line = line else prev_lines << line - return prev_lines + + break if generate_line_code(line) == self.line_code + + prev_lines.shift if prev_lines.length >= max_number_of_lines end end + + prev_lines end def diff_lines @@ -466,6 +493,10 @@ class Note < ActiveRecord::Base for_merge_request? && for_diff_line? end + def for_project_snippet? + noteable_type == "Snippet" + end + # override to return commits, which are not active record def noteable if for_commit? diff --git a/app/models/project.rb b/app/models/project.rb index e53b268c8ea..c45338bf4eb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -37,6 +37,8 @@ class Project < ActiveRecord::Base include Gitlab::ShellAdapter include Gitlab::VisibilityLevel include Gitlab::ConfigHelper + include Rails.application.routes.url_helpers + extend Gitlab::ConfigHelper extend Enumerize @@ -48,6 +50,12 @@ class Project < ActiveRecord::Base default_value_for :wall_enabled, false default_value_for :snippets_enabled, gitlab_config_features.snippets + # set last_activity_at to the same as created_at + after_create :set_last_activity_at + def set_last_activity_at + update_column(:last_activity_at, self.created_at) + end + ActsAsTaggableOn.strict_case_match = true acts_as_taggable_on :tags @@ -65,6 +73,7 @@ class Project < ActiveRecord::Base has_one :gitlab_ci_service, dependent: :destroy has_one :campfire_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy + has_one :irker_service, dependent: :destroy has_one :pivotaltracker_service, dependent: :destroy has_one :hipchat_service, dependent: :destroy has_one :flowdock_service, dependent: :destroy @@ -130,7 +139,7 @@ class Project < ActiveRecord::Base validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id validates :import_url, - format: { with: URI::regexp(%w(git http https)), message: 'should be a valid url' }, + format: { with: URI::regexp(%w(ssh git http https)), message: 'should be a valid url' }, if: :import? validates :star_count, numericality: { greater_than_or_equal_to: 0 } validate :check_limit, on: :create @@ -138,7 +147,7 @@ class Project < ActiveRecord::Base if: ->(project) { project.avatar && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } - mount_uploader :avatar, AttachmentUploader + mount_uploader :avatar, AvatarUploader # Scopes scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) } @@ -285,7 +294,7 @@ class Project < ActiveRecord::Base end def to_param - namespace.path + '/' + path + path end def web_url @@ -353,18 +362,28 @@ class Project < ActiveRecord::Base end def build_missing_services - available_services_names.each do |service_name| - service = services.find { |service| service.to_param == service_name } + services_templates = Service.where(template: true) + + Service.available_services_names.each do |service_name| + service = find_service(services, service_name) # If service is available but missing in db - # we should create an instance. Ex `create_gitlab_ci_service` - service = self.send :"create_#{service_name}_service" if service.nil? + if service.nil? + # We should check if template for the service exists + template = find_service(services_templates, service_name) + + if template.nil? + # If no template, we should create an instance. Ex `create_gitlab_ci_service` + service = self.send :"create_#{service_name}_service" + else + Service.create_from_template(self.id, template) + end + end end end - def available_services_names - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana - emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker) + def find_service(list, name) + list.find { |service| service.to_param == name } end def gitlab_ci? @@ -392,6 +411,14 @@ class Project < ActiveRecord::Base @avatar_file end + def avatar_url + if avatar.present? + [gitlab_config.url, avatar.url].join + elsif avatar_in_git + [gitlab_config.url, namespace_project_avatar_path(namespace, self)].join + end + end + # For compatibility with old code def code path @@ -452,8 +479,9 @@ class Project < ActiveRecord::Base end end - def execute_services(data) - services.select(&:active).each do |service| + def execute_services(data, hooks_scope = :push_hooks) + # Call only service hooks that are active for this scope + services.send(hooks_scope).each do |service| service.async_execute(data) end end diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index db1e7a2b1cb..d52214cdd69 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -2,16 +2,21 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # - require 'asana' class AsanaService < Service @@ -60,13 +65,19 @@ automatically inspected. Leave blank to include all branches.' ] end - def execute(push) + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + Asana.configure do |client| client.api_key = api_key end - user = push[:user_name] - branch = push[:ref].gsub('refs/heads/', '') + user = data[:user_name] + branch = Gitlab::Git.ref_name(data[:ref]) branch_restriction = restrict_to_branch.to_s @@ -78,7 +89,7 @@ automatically inspected. Leave blank to include all branches.' project_name = project.name_with_namespace push_msg = user + ' pushed to branch ' + branch + ' of ' + project_name - push[:commits].each do |commit| + data[:commits].each do |commit| check_commit(' ( ' + commit[:url] + ' ): ' + commit[:message], push_msg) end end diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index 0b90a14f39c..fb7e0c0fb0d 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class AssemblaService < Service @@ -37,8 +43,14 @@ class AssemblaService < Service ] end - def execute(push) + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}" - AssemblaService.post(url, body: { payload: push }.to_json, headers: { 'Content-Type' => 'application/json' }) + AssemblaService.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' }) end end diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 745609e5911..0100f1e4a10 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class BambooService < CiService @@ -68,6 +74,10 @@ class BambooService < CiService ] end + def supported_events + %w(push) + end + def build_info(sha) url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}") @@ -117,7 +127,9 @@ class BambooService < CiService end end - def execute(_data) + def execute(data) + return unless supported_events.include?(data[:object_kind]) + # Bamboo requires a GET and does not take any data. self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}", verify: false) diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildbox_service.rb index 0ab67b79fe4..270863c1576 100644 --- a/app/models/project_services/buildbox_service.rb +++ b/app/models/project_services/buildbox_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # require "addressable/uri" @@ -32,7 +38,13 @@ class BuildboxService < CiService hook.save end + def supported_events + %w(push) + end + def execute(data) + return unless supported_events.include?(data[:object_kind]) + service_hook.execute(data) end diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index 3116c311052..e591afdda64 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class CampfireService < Service @@ -36,11 +42,17 @@ class CampfireService < Service ] end - def execute(push_data) + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + room = gate.find_room_by_name(self.room) return true unless room - message = build_message(push_data) + message = build_message(data) room.speak(message) end @@ -52,7 +64,7 @@ class CampfireService < Service end def build_message(push) - ref = push[:ref].gsub("refs/heads/", "") + ref = Gitlab::Git.ref_name(push[:ref]) before = push[:before] after = push[:after] @@ -60,9 +72,9 @@ class CampfireService < Service message << "[#{project.name_with_namespace}] " message << "#{push[:user_name]} " - if before.include?('000000') + if Gitlab::Git.blank_ref?(before) message << "pushed new branch #{ref} \n" - elsif after.include?('000000') + elsif Gitlab::Git.blank_ref?(after) message << "removed branch #{ref} \n" else message << "pushed #{push[:total_commits_count]} commits to #{ref}. " diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index b1d5e49ede3..c6f6b4952c9 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # # Base class for CI services @@ -20,6 +25,10 @@ class CiService < Service :ci end + def supported_events + %w(push) + end + # Return complete url to build page # # Ex. diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 5845e2d3525..8d25f627870 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # class CustomIssueTrackerService < IssueTrackerService diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb index b9071b98295..acb5e7f1af5 100644 --- a/app/models/project_services/emails_on_push_service.rb +++ b/app/models/project_services/emails_on_push_service.rb @@ -2,17 +2,24 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # class EmailsOnPushService < Service + prop_accessor :send_from_committer_email + prop_accessor :disable_diffs prop_accessor :recipients validates :recipients, presence: true, if: :activated? @@ -28,12 +35,31 @@ class EmailsOnPushService < Service 'emails_on_push' end + def supported_events + %w(push) + end + def execute(push_data) - EmailsOnPushWorker.perform_async(project_id, recipients, push_data) + return unless supported_events.include?(push_data[:object_kind]) + + EmailsOnPushWorker.perform_async(project_id, recipients, push_data, send_from_committer_email?, disable_diffs?) + end + + def send_from_committer_email? + self.send_from_committer_email == "1" + end + + def disable_diffs? + self.disable_diffs == "1" end def fields + domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ") [ + { type: 'checkbox', name: 'send_from_committer_email', title: "Send from committer", + help: "Send notifications from the committer's email address if the domain is part of the domain GitLab is running on (e.g. #{domains})." }, + { type: 'checkbox', name: 'disable_diffs', title: "Disable code diffs", + help: "Don't include possibly sensitive code diffs in notification body." }, { type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' }, ] end diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 86705f5dabd..99e361dd6ed 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require "flowdock-git-hook" @@ -36,11 +41,17 @@ class FlowdockService < Service ] end - def execute(push_data) + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + Flowdock::Git.post( - push_data[:ref], - push_data[:before], - push_data[:after], + data[:ref], + data[:before], + data[:after], token: token, repo: project.repository.path_to_repo, repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}", diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index 18fdd204ecd..4e75bdfc953 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require "gemnasium/gitlab_service" @@ -37,11 +42,17 @@ class GemnasiumService < Service ] end - def execute(push_data) + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + Gemnasium::GitlabService.execute( - ref: push_data[:ref], - before: push_data[:before], - after: push_data[:after], + ref: data[:ref], + before: data[:before], + after: data[:after], token: token, api_key: api_key, repo: project.repository.path_to_repo diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 248f749b310..d81623625c9 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # class GitlabCiService < CiService @@ -17,8 +22,6 @@ class GitlabCiService < CiService validates :project_url, presence: true, if: :activated? validates :token, presence: true, if: :activated? - delegate :execute, to: :service_hook, prefix: nil - after_save :compose_service_hook, if: :activated? def compose_service_hook @@ -27,6 +30,16 @@ class GitlabCiService < CiService hook.save end + def supported_events + %w(push tag_push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + + service_hook.execute(data) + end + def commit_status_path(sha) project_url + "/commits/#{sha}/status.json?token=#{token}" end diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index 25e399883b7..84346350a6c 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class GitlabIssueTrackerService < IssueTrackerService @@ -26,14 +32,20 @@ class GitlabIssueTrackerService < IssueTrackerService end def project_url - project_issues_path(project) + "#{gitlab_url}#{namespace_project_issues_path(project.namespace, project)}" end def new_issue_url - new_project_issue_path project_id: project + "#{gitlab_url}#{new_namespace_project_issue_path(namespace_id: project.namespace, project_id: project)}" end def issue_url(iid) - "#{Gitlab.config.gitlab.url}#{project_issue_path(project_id: project, id: iid)}" + "#{gitlab_url}#{namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: iid)}" + end + + private + + def gitlab_url + Gitlab.config.gitlab.relative_url_root.chomp("/") if Gitlab.config.gitlab.relative_url_root end end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index c4c563b3cca..d264a56ebdf 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # class HipchatService < Service @@ -39,8 +44,14 @@ class HipchatService < Service ] end - def execute(push_data) - gate[room].send('GitLab', create_message(push_data)) + def supported_events + %w(push issue merge_request note tag_push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + + gate[room].send('GitLab', create_message(data)) end private @@ -51,22 +62,39 @@ class HipchatService < Service @gate ||= HipChat::Client.new(token, options) end - def create_message(push) - ref = push[:ref].gsub("refs/heads/", "") + def create_message(data) + object_kind = data[:object_kind] + + message = \ + case object_kind + when "push", "tag_push" + create_push_message(data) + when "issue" + create_issue_message(data) unless is_update?(data) + when "merge_request" + create_merge_request_message(data) unless is_update?(data) + when "note" + create_note_message(data) + end + end + + def create_push_message(push) + ref_type = Gitlab::Git.tag_ref?(push[:ref]) ? 'tag' : 'branch' + ref = Gitlab::Git.ref_name(push[:ref]) + before = push[:before] after = push[:after] message = "" message << "#{push[:user_name]} " - if before.include?('000000') - message << "pushed new branch <a href=\""\ - "#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\ - " to <a href=\"#{project.web_url}\">"\ - "#{project.name_with_namespace.gsub!(/\s/, "")}</a>\n" - elsif after.include?('000000') - message << "removed branch #{ref} from <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> \n" + if Gitlab::Git.blank_ref?(before) + message << "pushed new #{ref_type} <a href=\""\ + "#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\ + " to #{project_link}\n" + elsif Gitlab::Git.blank_ref?(after) + message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n" else - message << "pushed to branch <a href=\""\ + message << "pushed to #{ref_type} <a href=\""\ "#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a> " message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" @@ -82,4 +110,129 @@ class HipchatService < Service message end + + def format_body(body) + if body + body = body.truncate(200, separator: ' ', omission: '...') + end + + "<pre>#{body}</pre>" + end + + def create_issue_message(data) + user_name = data[:user][:name] + + obj_attr = data[:object_attributes] + obj_attr = HashWithIndifferentAccess.new(obj_attr) + title = obj_attr[:title] + state = obj_attr[:state] + issue_iid = obj_attr[:iid] + issue_url = obj_attr[:url] + description = obj_attr[:description] + + issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>" + message = "#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>" + + if description + description = format_body(description) + message << description + end + + message + end + + def create_merge_request_message(data) + user_name = data[:user][:name] + + obj_attr = data[:object_attributes] + obj_attr = HashWithIndifferentAccess.new(obj_attr) + merge_request_id = obj_attr[:iid] + source_branch = obj_attr[:source_branch] + target_branch = obj_attr[:target_branch] + state = obj_attr[:state] + description = obj_attr[:description] + title = obj_attr[:title] + + merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}" + merge_request_link = "<a href=\"#{merge_request_url}\">merge request ##{merge_request_id}</a>" + message = "#{user_name} #{state} #{merge_request_link} in " \ + "#{project_link}: <b>#{title}</b>" + + if description + description = format_body(description) + message << description + end + + message + end + + def format_title(title) + "<b>" + title.lines.first.chomp + "</b>" + end + + def create_note_message(data) + data = HashWithIndifferentAccess.new(data) + user_name = data[:user][:name] + + repo_attr = HashWithIndifferentAccess.new(data[:repository]) + + obj_attr = HashWithIndifferentAccess.new(data[:object_attributes]) + note = obj_attr[:note] + note_url = obj_attr[:url] + noteable_type = obj_attr[:noteable_type] + + case noteable_type + when "Commit" + commit_attr = HashWithIndifferentAccess.new(data[:commit]) + subject_desc = commit_attr[:id] + subject_desc = Commit.truncate_sha(subject_desc) + subject_type = "commit" + title = format_title(commit_attr[:message]) + when "Issue" + subj_attr = HashWithIndifferentAccess.new(data[:issue]) + subject_id = subj_attr[:iid] + subject_desc = "##{subject_id}" + subject_type = "issue" + title = format_title(subj_attr[:title]) + when "MergeRequest" + subj_attr = HashWithIndifferentAccess.new(data[:merge_request]) + subject_id = subj_attr[:iid] + subject_desc = "##{subject_id}" + subject_type = "merge request" + title = format_title(subj_attr[:title]) + when "Snippet" + subj_attr = HashWithIndifferentAccess.new(data[:snippet]) + subject_id = subj_attr[:id] + subject_desc = "##{subject_id}" + subject_type = "snippet" + title = format_title(subj_attr[:title]) + end + + subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>" + message = "#{user_name} commented on #{subject_html} in #{project_link}: " + message << title + + if note + note = format_body(note) + message << note + end + + message + end + + def project_name + project.name_with_namespace.gsub(/\s/, '') + end + + def project_url + project.web_url + end + + def project_link + "<a href=\"#{project_url}\">#{project_name}</a>" + end + + def is_update?(data) + data[:object_attributes][:action] == 'update' + end end diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb new file mode 100644 index 00000000000..2bddb7b881c --- /dev/null +++ b/app/models/project_services/irker_service.rb @@ -0,0 +1,163 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# + +require 'uri' + +class IrkerService < Service + prop_accessor :colorize_messages, :recipients, :channels + validates :recipients, presence: true, if: :activated? + validate :check_recipients_count, if: :activated? + + before_validation :get_channels + after_initialize :initialize_settings + + # Writer for RSpec tests + attr_writer :settings + + def initialize_settings + # See the documentation (doc/project_services/irker.md) for possible values + # here + @settings ||= { + server_ip: 'localhost', + server_port: 6659, + max_channels: 3, + default_irc_uri: nil + } + end + + def title + 'Irker (IRC gateway)' + end + + def description + 'Send IRC messages, on update, to a list of recipients through an Irker '\ + 'gateway.' + end + + def help + msg = 'Recipients have to be specified with a full URI: '\ + 'irc[s]://irc.network.net[:port]/#channel. Special cases: if you want '\ + 'the channel to be a nickname instead, append ",isnick" to the channel '\ + 'name; if the channel is protected by a secret password, append '\ + '"?key=secretpassword" to the URI.' + + unless @settings[:default_irc].nil? + msg += ' Note that a default IRC URI is provided by this service\'s '\ + "administrator: #{default_irc}. You can thus just give a channel name." + end + msg + end + + def to_param + 'irker' + end + + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + + IrkerWorker.perform_async(project_id, channels, + colorize_messages, data, @settings) + end + + def fields + [ + { type: 'textarea', name: 'recipients', + placeholder: 'Recipients/channels separated by whitespaces' }, + { type: 'checkbox', name: 'colorize_messages' }, + ] + end + + private + + def check_recipients_count + return true if recipients.nil? || recipients.empty? + + if recipients.split(/\s+/).count > max_chans + errors.add(:recipients, "are limited to #{max_chans}") + end + end + + def max_chans + @settings[:max_channels] + end + + def get_channels + return true unless :activated? + return true if recipients.nil? || recipients.empty? + + map_recipients + + errors.add(:recipients, 'are all invalid') if channels.empty? + true + end + + def map_recipients + self.channels = recipients.split(/\s+/).map do |recipient| + format_channel default_irc_uri, recipient + end + channels.reject! &:nil? + end + + def default_irc_uri + default_irc = @settings[:default_irc_uri] + if !(default_irc.nil? || default_irc[-1] == '/') + default_irc += '/' + end + default_irc + end + + def format_channel(default_irc, recipient) + cnt = 0 + url = nil + + # Try to parse the chan as a full URI + begin + uri = URI.parse(recipient) + raise URI::InvalidURIError if uri.scheme.nil? && cnt == 0 + rescue URI::InvalidURIError + unless default_irc.nil? + cnt += 1 + recipient = "#{default_irc}#{recipient}" + retry if cnt == 1 + end + else + url = consider_uri uri + end + url + end + + def consider_uri(uri) + # Authorize both irc://domain.com/#chan and irc://domain.com/chan + if uri.is_a?(URI) && uri.scheme[/^ircs?$/] && !uri.path.nil? + # Do not authorize irc://domain.com/ + 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 diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index b19c02bab44..8e90c44d103 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class IssueTrackerService < Service @@ -24,18 +30,6 @@ class IssueTrackerService < Service false end - def project_url - # implement inside child - end - - def issues_url - # implement inside child - end - - def new_issue_url - # implement inside child - end - def issue_url(iid) self.issues_url.gsub(':id', iid.to_s) end @@ -54,9 +48,9 @@ class IssueTrackerService < Service if enabled_in_gitlab_config self.properties = { title: issues_tracker['title'], - project_url: set_project_url, - issues_url: issues_tracker['issues_url'], - new_issue_url: issues_tracker['new_issue_url'] + project_url: add_issues_tracker_id(issues_tracker['project_url']), + issues_url: add_issues_tracker_id(issues_tracker['issues_url']), + new_issue_url: add_issues_tracker_id(issues_tracker['new_issue_url']) } else self.properties = {} @@ -64,6 +58,35 @@ class IssueTrackerService < Service end end + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + + message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again." + result = false + + begin + url = URI.parse(self.project_url) + + if url.host && url.port + http = Net::HTTP.start(url.host, url.port, { open_timeout: 5, read_timeout: 5 }) + response = http.head("/") + + if response + message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}" + result = true + end + end + rescue Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error + message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}" + end + Rails.logger.info(message) + result + end + private def enabled_in_gitlab_config @@ -76,13 +99,15 @@ class IssueTrackerService < Service Gitlab.config.issues_tracker[to_param] end - def set_project_url - id = self.project.issues_tracker_id + def add_issues_tracker_id(url) + if self.project + id = self.project.issues_tracker_id - if id - issues_tracker['project_url'].gsub(":issues_tracker_id", id) - else - issues_tracker['project_url'] + if id + url = url.gsub(":issues_tracker_id", id) + end end + + url end end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 7a32b0e8c2c..fcd9dc2f336 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -2,20 +2,40 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class JiraService < IssueTrackerService + include Rails.application.routes.url_helpers prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url + def help + issue_tracker_link = help_page_path("integration", "external-issue-tracker") + + line1 = "Setting `project_url`, `issues_url` and `new_issue_url` will "\ + "allow a user to easily navigate to the Jira issue tracker. "\ + "See the [integration doc](#{issue_tracker_link}) for details." + + line2 = 'Support for referencing commits and automatic closing of Jira issues directly ' \ + 'from GitLab is [available in GitLab EE.](http://doc.gitlab.com/ee/integration/jira.html)' + + [line1, line2].join("\n\n") + end + def title if self.properties && self.properties['title'].present? self.properties['title'] diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index 09e114f9cca..ade9ee97873 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class PivotaltrackerService < Service @@ -36,9 +42,15 @@ class PivotaltrackerService < Service ] end - def execute(push) + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + url = 'https://www.pivotaltracker.com/services/v5/source_commits' - push[:commits].each do |commit| + data[:commits].each do |commit| message = { 'source_commit' => { 'commit_id' => commit[:id], diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index a9b23f97ba0..53edf522e9a 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class PushoverService < Service @@ -75,21 +81,27 @@ class PushoverService < Service ] end - def execute(push_data) - ref = push_data[:ref].gsub('refs/heads/', '') - before = push_data[:before] - after = push_data[:after] + def supported_events + %w(push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + + ref = Gitlab::Git.ref_name(data[:ref]) + before = data[:before] + after = data[:after] - if before.include?('000000') - message = "#{push_data[:user_name]} pushed new branch \"#{ref}\"." - elsif after.include?('000000') - message = "#{push_data[:user_name]} deleted branch \"#{ref}\"." + if Gitlab::Git.blank_ref?(before) + message = "#{data[:user_name]} pushed new branch \"#{ref}\"." + elsif Gitlab::Git.blank_ref?(after) + message = "#{data[:user_name]} deleted branch \"#{ref}\"." else - message = "#{push_data[:user_name]} push to branch \"#{ref}\"." + message = "#{data[:user_name]} push to branch \"#{ref}\"." end - if push_data[:total_commits_count] > 0 - message << "\nTotal commits count: #{push_data[:total_commits_count]}" + if data[:total_commits_count] > 0 + message << "\nTotal commits count: #{data[:total_commits_count]}" end pushover_data = { @@ -99,7 +111,7 @@ class PushoverService < Service priority: priority, title: "#{project.name_with_namespace}", message: message, - url: push_data[:repository][:homepage], + url: data[:repository][:homepage], url_title: "See project #{project.name_with_namespace}" } diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb index 547b2401832..dd9ba97ee1f 100644 --- a/app/models/project_services/redmine_service.rb +++ b/app/models/project_services/redmine_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class RedmineService < IssueTrackerService diff --git a/app/models/project_services/slack_message.rb b/app/models/project_services/slack_message.rb deleted file mode 100644 index 6c6446db45f..00000000000 --- a/app/models/project_services/slack_message.rb +++ /dev/null @@ -1,110 +0,0 @@ -require 'slack-notifier' - -class SlackMessage - attr_reader :after - attr_reader :before - attr_reader :commits - attr_reader :project_name - attr_reader :project_url - attr_reader :ref - attr_reader :username - - def initialize(params) - @after = params.fetch(:after) - @before = params.fetch(:before) - @commits = params.fetch(:commits, []) - @project_name = params.fetch(:project_name) - @project_url = params.fetch(:project_url) - @ref = params.fetch(:ref).gsub('refs/heads/', '') - @username = params.fetch(:user_name) - end - - def pretext - format(message) - end - - def attachments - return [] if new_branch? || removed_branch? - - commit_message_attachments - end - - private - - def message - if new_branch? - new_branch_message - elsif removed_branch? - removed_branch_message - else - push_message - end - end - - def format(string) - Slack::Notifier::LinkFormatter.format(string) - end - - def new_branch_message - "#{username} pushed new branch #{branch_link} to #{project_link}" - end - - def removed_branch_message - "#{username} removed branch #{ref} from #{project_link}" - end - - def push_message - "#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})" - end - - def commit_messages - commits.each_with_object('') do |commit, str| - str << compose_commit_message(commit) - end.chomp - end - - def commit_message_attachments - [{ text: format(commit_messages), color: attachment_color }] - end - - def compose_commit_message(commit) - author = commit.fetch(:author).fetch(:name) - id = commit.fetch(:id)[0..8] - message = commit.fetch(:message) - url = commit.fetch(:url) - - "[#{id}](#{url}): #{message} - #{author}\n" - end - - def new_branch? - before.include?('000000') - end - - def removed_branch? - after.include?('000000') - end - - def branch_url - "#{project_url}/commits/#{ref}" - end - - def compare_url - "#{project_url}/compare/#{before}...#{after}" - end - - def branch_link - "[#{ref}](#{branch_url})" - end - - def project_link - "[#{project_name}](#{project_url})" - end - - def compare_link - "[Compare changes](#{compare_url})" - end - - def attachment_color - '#345' - end -end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 963f5440b6f..36d9874edd3 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -2,18 +2,24 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class SlackService < Service - prop_accessor :webhook + prop_accessor :webhook, :username, :channel validates :webhook, presence: true, if: :activated? def title @@ -30,20 +36,52 @@ class SlackService < Service def fields [ - { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' } + { type: 'text', name: 'webhook', + placeholder: 'https://hooks.slack.com/services/...' }, + { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'text', name: 'channel', placeholder: '#channel' } ] end - def execute(push_data) + def supported_events + %w(push issue merge_request note tag_push) + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) return unless webhook.present? - message = SlackMessage.new(push_data.merge( + object_kind = data[:object_kind] + + data = data.merge( project_url: project_url, project_name: project_name - )) + ) + + # WebHook events often have an 'update' event that follows a 'open' or + # 'close' action. Ignore update events for now to prevent duplicate + # messages from arriving. + + message = \ + case object_kind + when "push", "tag_push" + PushMessage.new(data) + when "issue" + IssueMessage.new(data) unless is_update?(data) + when "merge_request" + MergeMessage.new(data) unless is_update?(data) + when "note" + NoteMessage.new(data) + end - notifier = Slack::Notifier.new(webhook) - notifier.ping(message.pretext, attachments: message.attachments) + opt = {} + opt[:channel] = channel if channel + opt[:username] = username if username + + if message + notifier = Slack::Notifier.new(webhook, opt) + notifier.ping(message.pretext, attachments: message.attachments) + end end private @@ -55,4 +93,13 @@ class SlackService < Service def project_url project.web_url end + + def is_update?(data) + data[:object_attributes][:action] == 'update' + end end + +require "slack_service/issue_message" +require "slack_service/push_message" +require "slack_service/merge_message" +require "slack_service/note_message" diff --git a/app/models/project_services/slack_service/base_message.rb b/app/models/project_services/slack_service/base_message.rb new file mode 100644 index 00000000000..aa00d6061a1 --- /dev/null +++ b/app/models/project_services/slack_service/base_message.rb @@ -0,0 +1,31 @@ +require 'slack-notifier' + +class SlackService + class BaseMessage + def initialize(params) + raise NotImplementedError + end + + def pretext + format(message) + end + + def attachments + raise NotImplementedError + end + + private + + def message + raise NotImplementedError + end + + def format(string) + Slack::Notifier::LinkFormatter.format(string) + end + + def attachment_color + '#345' + end + end +end diff --git a/app/models/project_services/slack_service/issue_message.rb b/app/models/project_services/slack_service/issue_message.rb new file mode 100644 index 00000000000..5af24a80609 --- /dev/null +++ b/app/models/project_services/slack_service/issue_message.rb @@ -0,0 +1,56 @@ +class SlackService + class IssueMessage < BaseMessage + attr_reader :user_name + attr_reader :title + attr_reader :project_name + attr_reader :project_url + attr_reader :issue_iid + attr_reader :issue_url + attr_reader :action + attr_reader :state + attr_reader :description + + def initialize(params) + @user_name = params[:user][:name] + @project_name = params[:project_name] + @project_url = params[:project_url] + + obj_attr = params[:object_attributes] + obj_attr = HashWithIndifferentAccess.new(obj_attr) + @title = obj_attr[:title] + @issue_iid = obj_attr[:iid] + @issue_url = obj_attr[:url] + @action = obj_attr[:action] + @state = obj_attr[:state] + @description = obj_attr[:description] + end + + def attachments + return [] unless opened_issue? + + description_message + end + + private + + def message + "#{user_name} #{state} #{issue_link} in #{project_link}: *#{title}*" + end + + def opened_issue? + action == "open" + end + + def description_message + [{ text: format(description), color: attachment_color }] + end + + def project_link + "[#{project_name}](#{project_url})" + end + + def issue_link + "[issue ##{issue_iid}](#{issue_url})" + end + end +end diff --git a/app/models/project_services/slack_service/merge_message.rb b/app/models/project_services/slack_service/merge_message.rb new file mode 100644 index 00000000000..e792c258f73 --- /dev/null +++ b/app/models/project_services/slack_service/merge_message.rb @@ -0,0 +1,60 @@ +class SlackService + class MergeMessage < BaseMessage + attr_reader :user_name + attr_reader :project_name + attr_reader :project_url + attr_reader :merge_request_id + attr_reader :source_branch + attr_reader :target_branch + attr_reader :state + attr_reader :title + + def initialize(params) + @user_name = params[:user][:name] + @project_name = params[:project_name] + @project_url = params[:project_url] + + obj_attr = params[:object_attributes] + obj_attr = HashWithIndifferentAccess.new(obj_attr) + @merge_request_id = obj_attr[:iid] + @source_branch = obj_attr[:source_branch] + @target_branch = obj_attr[:target_branch] + @state = obj_attr[:state] + @title = format_title(obj_attr[:title]) + end + + def pretext + format(message) + end + + def attachments + [] + end + + private + + def format_title(title) + '*' + title.lines.first.chomp + '*' + end + + def message + merge_request_message + end + + def project_link + "[#{project_name}](#{project_url})" + end + + def merge_request_message + "#{user_name} #{state} #{merge_request_link} in #{project_link}: #{title}" + end + + def merge_request_link + "[merge request ##{merge_request_id}](#{merge_request_url})" + end + + def merge_request_url + "#{project_url}/merge_requests/#{merge_request_id}" + end + end +end diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb new file mode 100644 index 00000000000..074478b292d --- /dev/null +++ b/app/models/project_services/slack_service/note_message.rb @@ -0,0 +1,82 @@ +class SlackService + class NoteMessage < BaseMessage + attr_reader :message + attr_reader :user_name + attr_reader :project_name + attr_reader :project_link + attr_reader :note + attr_reader :note_url + attr_reader :title + + def initialize(params) + params = HashWithIndifferentAccess.new(params) + @user_name = params[:user][:name] + @project_name = params[:project_name] + @project_url = params[:project_url] + + obj_attr = params[:object_attributes] + obj_attr = HashWithIndifferentAccess.new(obj_attr) + @note = obj_attr[:note] + @note_url = obj_attr[:url] + noteable_type = obj_attr[:noteable_type] + + case noteable_type + when "Commit" + create_commit_note(HashWithIndifferentAccess.new(params[:commit])) + when "Issue" + create_issue_note(HashWithIndifferentAccess.new(params[:issue])) + when "MergeRequest" + create_merge_note(HashWithIndifferentAccess.new(params[:merge_request])) + when "Snippet" + create_snippet_note(HashWithIndifferentAccess.new(params[:snippet])) + end + end + + def attachments + description_message + end + + private + + def format_title(title) + title.lines.first.chomp + end + + def create_commit_note(commit) + commit_sha = commit[:id] + commit_sha = Commit.truncate_sha(commit_sha) + commit_link = "[commit #{commit_sha}](#{@note_url})" + title = format_title(commit[:message]) + @message = "#{@user_name} commented on #{commit_link} in #{project_link}: *#{title}*" + end + + def create_issue_note(issue) + issue_iid = issue[:iid] + note_link = "[issue ##{issue_iid}](#{@note_url})" + title = format_title(issue[:title]) + @message = "#{@user_name} commented on #{note_link} in #{project_link}: *#{title}*" + end + + def create_merge_note(merge_request) + merge_request_id = merge_request[:iid] + merge_request_link = "[merge request ##{merge_request_id}](#{@note_url})" + title = format_title(merge_request[:title]) + @message = "#{@user_name} commented on #{merge_request_link} in #{project_link}: *#{title}*" + end + + def create_snippet_note(snippet) + snippet_id = snippet[:id] + snippet_link = "[snippet ##{snippet_id}](#{@note_url})" + title = format_title(snippet[:title]) + @message = "#{@user_name} commented on #{snippet_link} in #{project_link}: *#{title}*" + end + + def description_message + [{ text: format(@note), color: attachment_color }] + end + + def project_link + "[#{@project_name}](#{@project_url})" + end + end +end diff --git a/app/models/project_services/slack_service/push_message.rb b/app/models/project_services/slack_service/push_message.rb new file mode 100644 index 00000000000..b26f3e9ddce --- /dev/null +++ b/app/models/project_services/slack_service/push_message.rb @@ -0,0 +1,110 @@ +class SlackService + class PushMessage < BaseMessage + attr_reader :after + attr_reader :before + attr_reader :commits + attr_reader :project_name + attr_reader :project_url + attr_reader :ref + attr_reader :ref_type + attr_reader :user_name + + def initialize(params) + @after = params[:after] + @before = params[:before] + @commits = params.fetch(:commits, []) + @project_name = params[:project_name] + @project_url = params[:project_url] + @ref_type = Gitlab::Git.tag_ref?(params[:ref]) ? 'tag' : 'branch' + @ref = Gitlab::Git.ref_name(params[:ref]) + @user_name = params[:user_name] + end + + def pretext + format(message) + end + + def attachments + return [] if new_branch? || removed_branch? + + commit_message_attachments + end + + private + + def message + if new_branch? + new_branch_message + elsif removed_branch? + removed_branch_message + else + push_message + end + end + + def format(string) + Slack::Notifier::LinkFormatter.format(string) + end + + def new_branch_message + "#{user_name} pushed new #{ref_type} #{branch_link} to #{project_link}" + end + + def removed_branch_message + "#{user_name} removed #{ref_type} #{ref} from #{project_link}" + end + + def push_message + "#{user_name} pushed to #{ref_type} #{branch_link} of #{project_link} (#{compare_link})" + end + + def commit_messages + commits.map { |commit| compose_commit_message(commit) }.join("\n") + end + + def commit_message_attachments + [{ text: format(commit_messages), color: attachment_color }] + end + + def compose_commit_message(commit) + author = commit[:author][:name] + id = Commit.truncate_sha(commit[:id]) + message = commit[:message] + url = commit[:url] + + "[#{id}](#{url}): #{message} - #{author}" + end + + def new_branch? + Gitlab::Git.blank_ref?(before) + end + + def removed_branch? + Gitlab::Git.blank_ref?(after) + end + + def branch_url + "#{project_url}/commits/#{ref}" + end + + def compare_url + "#{project_url}/compare/#{before}...#{after}" + end + + def branch_link + "[#{ref}](#{branch_url})" + end + + def project_link + "[#{project_name}](#{project_url})" + end + + def compare_link + "[Compare changes](#{compare_url})" + end + + def attachment_color + '#345' + end + end +end diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 287f5c0e84e..7403e19da9a 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -2,14 +2,20 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null # class TeamcityService < CiService @@ -56,6 +62,10 @@ class TeamcityService < CiService 'teamcity' end + def supported_events + %w(push) + end + def fields [ { type: 'text', name: 'teamcity_url', @@ -115,12 +125,14 @@ class TeamcityService < CiService end def execute(data) + return unless supported_events.include?(data[:object_kind]) + auth = { username: username, password: password, } - branch = data[:ref] + branch = Gitlab::Git.ref_name(data[:ref]) self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue", body: "<build branchName=\"#{branch}\">"\ diff --git a/app/models/repository.rb b/app/models/repository.rb index 4e45a6723b8..6117db418a7 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -146,7 +146,8 @@ class Repository end def timestamps_by_user_log(user) - args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --pretty=format:%cd --date=short) + author_emails = '(' + user.all_emails.map{ |e| Regexp.escape(e) }.join('|') + ')' + args = %W(git log -E --author=#{author_emails} --since=#{(Date.today - 1.year).to_s} --branches --pretty=format:%cd --date=short) dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n") if dates.present? @@ -238,7 +239,7 @@ class Repository end def last_commit_for_path(sha, path) - args = %W(git rev-list --max-count 1 #{sha} -- #{path}) + args = %W(git rev-list --max-count=1 #{sha} -- #{path}) sha = Gitlab::Popen.popen(args, path_to_repo).first.strip commit(sha) end diff --git a/app/models/service.rb b/app/models/service.rb index caabe8e971d..33734e97c55 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # # To add new service you should build a class inherited from Service @@ -19,20 +24,35 @@ class Service < ActiveRecord::Base serialize :properties, JSON default_value_for :active, false + default_value_for :push_events, true + default_value_for :issues_events, true + default_value_for :merge_requests_events, true + default_value_for :tag_push_events, true + default_value_for :note_events, true after_initialize :initialize_properties belongs_to :project has_one :service_hook - validates :project_id, presence: true + validates :project_id, presence: true, unless: Proc.new { |service| service.template? } scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') } + scope :push_hooks, -> { where(push_events: true, active: true) } + scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) } + scope :issue_hooks, -> { where(issues_events: true, active: true) } + scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) } + scope :note_hooks, -> { where(note_events: true, active: true) } + def activated? active end + def template? + template + end + def category :common end @@ -62,6 +82,10 @@ class Service < ActiveRecord::Base [] end + def supported_events + %w(push tag_push issue merge_request) + end + def execute # implement inside child end @@ -87,6 +111,8 @@ class Service < ActiveRecord::Base end def async_execute(data) + return unless supported_events.include?(data[:object_kind]) + Sidekiq::Client.enqueue(ProjectServiceWorker, id, data) end @@ -94,7 +120,16 @@ class Service < ActiveRecord::Base self.category == :issue_tracker end - def self.issue_tracker_service_list - Service.select(&:issue_tracker?).map{ |s| s.to_param } + def self.available_services_names + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana + emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira + redmine custom_issue_tracker irker) + end + + def self.create_from_template(project_id, template) + service = template.dup + service.template = false + service.project_id = project_id + service if service.save end end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 82c1ab94446..3fb2ec1d66c 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -59,6 +59,10 @@ class Snippet < ActiveRecord::Base content end + def hook_attrs + attributes + end + def size 0 end diff --git a/app/models/user.rb b/app/models/user.rb index 3a7dfabeafe..0d40ac8309e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,48 +2,53 @@ # # Table name: users # -# id :integer not null, primary key -# email :string(255) default(""), not null -# encrypted_password :string(255) default(""), not null -# reset_password_token :string(255) -# reset_password_sent_at :datetime -# remember_created_at :datetime -# sign_in_count :integer default(0) -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) -# created_at :datetime -# updated_at :datetime -# name :string(255) -# admin :boolean default(FALSE), not null -# projects_limit :integer default(10) -# skype :string(255) default(""), not null -# linkedin :string(255) default(""), not null -# twitter :string(255) default(""), not null -# authentication_token :string(255) -# theme_id :integer default(1), not null -# bio :string(255) -# failed_attempts :integer default(0) -# locked_at :datetime -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null -# password_expires_at :datetime -# created_by_id :integer -# avatar :string(255) -# confirmation_token :string(255) -# confirmed_at :datetime -# confirmation_sent_at :datetime -# unconfirmed_email :string(255) -# hide_no_ssh_key :boolean default(FALSE) -# website_url :string(255) default(""), not null -# last_credential_check_at :datetime -# github_access_token :string(255) -# notification_email :string(255) +# id :integer not null, primary key +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# reset_password_token :string(255) +# reset_password_sent_at :datetime +# remember_created_at :datetime +# sign_in_count :integer default(0) +# current_sign_in_at :datetime +# last_sign_in_at :datetime +# current_sign_in_ip :string(255) +# last_sign_in_ip :string(255) +# created_at :datetime +# updated_at :datetime +# name :string(255) +# admin :boolean default(FALSE), not null +# projects_limit :integer default(10) +# skype :string(255) default(""), not null +# linkedin :string(255) default(""), not null +# twitter :string(255) default(""), not null +# authentication_token :string(255) +# theme_id :integer default(1), not null +# bio :string(255) +# failed_attempts :integer default(0) +# locked_at :datetime +# username :string(255) +# can_create_group :boolean default(TRUE), not null +# can_create_team :boolean default(TRUE), not null +# state :string(255) +# color_scheme_id :integer default(1), not null +# notification_level :integer default(1), not null +# password_expires_at :datetime +# created_by_id :integer +# last_credential_check_at :datetime +# avatar :string(255) +# confirmation_token :string(255) +# confirmed_at :datetime +# confirmation_sent_at :datetime +# unconfirmed_email :string(255) +# hide_no_ssh_key :boolean default(FALSE) +# website_url :string(255) default(""), not null +# github_access_token :string(255) +# gitlab_access_token :string(255) +# notification_email :string(255) +# hide_no_password :boolean default(FALSE) +# password_automatically_set :boolean default(FALSE) +# bitbucket_access_token :string(255) +# bitbucket_access_token_secret :string(255) # require 'carrierwave/orm/activerecord' @@ -54,13 +59,13 @@ class User < ActiveRecord::Base include Gitlab::ConfigHelper include TokenAuthenticatable extend Gitlab::ConfigHelper - extend Gitlab::CurrentSettings + include Gitlab::CurrentSettings default_value_for :admin, false default_value_for :can_create_group, gitlab_config.default_can_create_group default_value_for :can_create_team, false default_value_for :hide_no_ssh_key, false - default_value_for :projects_limit, current_application_settings.default_projects_limit + default_value_for :hide_no_password, false default_value_for :theme_id, gitlab_config.default_theme devise :database_authenticatable, :lockable, :async, @@ -139,6 +144,7 @@ class User < ActiveRecord::Base before_save :ensure_authentication_token after_save :ensure_namespace_correct + after_initialize :set_projects_limit after_create :post_create_hook after_destroy :post_destroy_hook @@ -148,24 +154,6 @@ class User < ActiveRecord::Base delegate :path, to: :namespace, allow_nil: true, prefix: true state_machine :state, initial: :active do - after_transition any => :blocked do |user, transition| - # Remove user from all projects and - user.project_members.find_each do |membership| - # skip owned resources - next if membership.project.owner == user - - return false unless membership.destroy - end - - # Remove user from all groups - user.group_members.find_each do |membership| - # skip owned resources - next if membership.group.last_owner?(user) - - return false unless membership.destroy - end - end - event :block do transition active: :blocked end @@ -175,7 +163,7 @@ class User < ActiveRecord::Base end end - mount_uploader :avatar, AttachmentUploader + mount_uploader :avatar, AvatarUploader # Scopes scope :admins, -> { where(admin: true) } @@ -243,6 +231,22 @@ class User < ActiveRecord::Base def build_user(attrs = {}) User.new(attrs) end + + def clean_username(username) + username.gsub!(/@.*\z/, "") + username.gsub!(/\.git\z/, "") + username.gsub!(/\A-/, "") + username.gsub!(/[^a-zA-Z0-9_\-\.]/, "") + + counter = 0 + base = username + while User.by_login(username).present? || Namespace.by_path(username).present? + counter += 1 + username = "#{base}#{counter}" + end + + username + end end # @@ -274,7 +278,8 @@ class User < ActiveRecord::Base def namespace_uniq namespace_name = self.username - if Namespace.find_by(path: namespace_name) + existing_namespace = Namespace.by_path(namespace_name) + if existing_namespace && existing_namespace != self.namespace self.errors.add :username, "already exists" end end @@ -331,6 +336,10 @@ class User < ActiveRecord::Base keys.count == 0 end + def require_password? + password_automatically_set? && !ldap_user? + end + def can_change_username? gitlab_config.username_changing_enabled end @@ -440,10 +449,17 @@ class User < ActiveRecord::Base def set_notification_email if self.notification_email.blank? || !self.all_emails.include?(self.notification_email) - self.notification_email = self.email + self.notification_email = self.email end end + def set_projects_limit + connection_default_value_defined = new_record? && !projects_limit_changed? + return unless self.projects_limit.nil? || connection_default_value_defined + + self.projects_limit = current_application_settings.default_projects_limit + end + def requires_ldap_check? if !Gitlab.config.ldap.enabled false @@ -540,7 +556,7 @@ class User < ActiveRecord::Base def post_create_hook log_info("User \"#{self.name}\" (#{self.email}) was created") - notification_service.new_user(self, @reset_token) + notification_service.new_user(self, @reset_token) if self.created_by_id system_hook_service.execute_hooks_for(self, :create) end @@ -588,4 +604,15 @@ class User < ActiveRecord::Base def oauth_authorized_tokens Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil) end + + def contributed_projects_ids + Event.where(author_id: self). + where("created_at > ?", Time.now - 1.year). + where("action = :pushed OR (target_type = 'MergeRequest' AND action = :created)", + pushed: Event::PUSHED, created: Event::CREATED). + reorder(project_id: :desc). + select(:project_id). + uniq + .map(&:project_id) + end end diff --git a/app/services/base_service.rb b/app/services/base_service.rb index bb51795df7c..52ab29f1492 100644 --- a/app/services/base_service.rb +++ b/app/services/base_service.rb @@ -37,11 +37,14 @@ class BaseService private - def error(message) - { + def error(message, http_status = nil) + result = { message: message, status: :error } + + result[:http_status] = http_status if http_status + result end def success diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb index 901f67bafb3..5e971c7891c 100644 --- a/app/services/create_branch_service.rb +++ b/app/services/create_branch_service.rb @@ -17,7 +17,7 @@ class CreateBranchService < BaseService new_branch = repository.find_branch(branch_name) if new_branch - Event.create_ref_event(project, current_user, new_branch, 'add') + EventCreateService.new.push_ref(project, current_user, new_branch, 'add') return success(new_branch) else return error('Invalid reference name') diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 041c2287c36..dfc5677c9d4 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -21,12 +21,12 @@ class CreateTagService < BaseService new_tag = repository.find_tag(tag_name) if new_tag - if project.gitlab_ci? - push_data = create_push_data(project, current_user, new_tag) - project.gitlab_ci_service.async_execute(push_data) - end + EventCreateService.new.push_ref(project, current_user, new_tag, 'add', Gitlab::Git::TAG_REF_PREFIX) + + push_data = create_push_data(project, current_user, new_tag) + project.execute_hooks(push_data.dup, :tag_push_hooks) + project.execute_services(push_data.dup, :tag_push_hooks) - Event.create_ref_event(project, current_user, new_tag, 'add', 'refs/tags') success(new_tag) else error('Invalid reference name') @@ -40,7 +40,9 @@ class CreateTagService < BaseService end def create_push_data(project, user, tag) - Gitlab::PushDataBuilder. - build(project, user, Gitlab::Git::BLANK_SHA, tag.target, 'refs/tags/' + tag.name, []) + data = Gitlab::PushDataBuilder. + build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", []) + data[:object_kind] = "tag_push" + data end end diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index cae6327fe72..c26aee2b0aa 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -25,7 +25,7 @@ class DeleteBranchService < BaseService end if repository.rm_branch(branch_name) - Event.create_ref_event(project, current_user, branch, 'rm') + EventCreateService.new.push_ref(project, current_user, branch, 'rm') success('Branch was removed') else return error('Failed to remove branch') diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb index 8d8a5873e62..dc52d6d89df 100644 --- a/app/services/event_create_service.rb +++ b/app/services/event_create_service.rb @@ -7,58 +7,98 @@ # class EventCreateService def open_issue(issue, current_user) - create_event(issue, current_user, Event::CREATED) + create_record_event(issue, current_user, Event::CREATED) end def close_issue(issue, current_user) - create_event(issue, current_user, Event::CLOSED) + create_record_event(issue, current_user, Event::CLOSED) end def reopen_issue(issue, current_user) - create_event(issue, current_user, Event::REOPENED) + create_record_event(issue, current_user, Event::REOPENED) end def open_mr(merge_request, current_user) - create_event(merge_request, current_user, Event::CREATED) + create_record_event(merge_request, current_user, Event::CREATED) end def close_mr(merge_request, current_user) - create_event(merge_request, current_user, Event::CLOSED) + create_record_event(merge_request, current_user, Event::CLOSED) end def reopen_mr(merge_request, current_user) - create_event(merge_request, current_user, Event::REOPENED) + create_record_event(merge_request, current_user, Event::REOPENED) end def merge_mr(merge_request, current_user) - create_event(merge_request, current_user, Event::MERGED) + create_record_event(merge_request, current_user, Event::MERGED) end def open_milestone(milestone, current_user) - create_event(milestone, current_user, Event::CREATED) + create_record_event(milestone, current_user, Event::CREATED) end def close_milestone(milestone, current_user) - create_event(milestone, current_user, Event::CLOSED) + create_record_event(milestone, current_user, Event::CLOSED) end def reopen_milestone(milestone, current_user) - create_event(milestone, current_user, Event::REOPENED) + create_record_event(milestone, current_user, Event::REOPENED) end def leave_note(note, current_user) - create_event(note, current_user, Event::COMMENTED) + create_record_event(note, current_user, Event::COMMENTED) + end + + def join_project(project, current_user) + create_event(project, current_user, Event::JOINED) + end + + def leave_project(project, current_user) + create_event(project, current_user, Event::LEFT) + end + + def create_project(project, current_user) + create_event(project, current_user, Event::CREATED) + end + + def push_ref(project, current_user, ref, action = 'add', prefix = Gitlab::Git::BRANCH_REF_PREFIX) + commit = project.repository.commit(ref.target) + + if action.to_s == 'add' + before = Gitlab::Git::BLANK_SHA + after = commit.id + else + before = commit.id + after = Gitlab::Git::BLANK_SHA + end + + data = { + ref: "#{prefix}#{ref.name}", + before: before, + after: after + } + + push(project, current_user, data) + end + + def push(project, current_user, push_data) + create_event(project, current_user, Event::PUSHED, data: push_data) end private - def create_event(record, current_user, status) - Event.create( - project: record.project, - target_id: record.id, - target_type: record.class.name, + def create_record_event(record, current_user, status) + create_event(record.project, current_user, status, target_id: record.id, target_type: record.class.name) + end + + def create_event(project, current_user, status, attributes = {}) + attributes.reverse_merge!( + project: project, action: status, author_id: current_user.id ) + + Event.create(attributes) end end diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index 2c457ef2cef..de5322e990a 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -38,7 +38,8 @@ module Files created_successfully = new_file_action.commit!( params[:content], params[:commit_message], - params[:encoding] + params[:encoding], + params[:new_branch] ) if created_successfully diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index b4986e1c5c6..328cf3a4b06 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -20,17 +20,20 @@ module Files end edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path) - created_successfully = edit_file_action.commit!( + edit_file_action.commit!( params[:content], params[:commit_message], - params[:encoding] + params[:encoding], + params[:new_branch] ) - if created_successfully - success - else - error("Your changes could not be committed. Maybe the file was changed by another process or there was nothing to commit?") - end + success + rescue Gitlab::Satellite::CheckoutFailed => ex + error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400) + rescue Gitlab::Satellite::CommitFailed => ex + error("Your changes could not be committed. Maybe there was nothing to commit?", 409) + rescue Gitlab::Satellite::PushFailed => ex + error("Your changes could not be committed. Maybe the file was changed by another process?", 409) end end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index c775f79ec29..bfabfd7ade3 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -42,8 +42,10 @@ class GitPushService # as a heuristic. This may include more commits than are actually pushed, but # that shouldn't matter because we check for existing cross-references later. @push_commits = project.repository.commits_between(project.default_branch, newrev) + + # don't process commits for the initial push to the default branch + process_commit_messages(ref) end - process_commit_messages(ref) elsif push_to_existing_branch?(ref, oldrev) # Collect data for this git push @push_commits = project.repository.commits_between(oldrev, newrev) @@ -52,23 +54,14 @@ class GitPushService end @push_data = post_receive_data(oldrev, newrev, ref) - create_push_event(@push_data) + EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :push_hooks) - project.execute_services(@push_data.dup) + project.execute_services(@push_data.dup, :push_hooks) end end protected - def create_push_event(push_data) - Event.create!( - project: project, - action: Event::PUSHED, - data: push_data, - author_id: push_data[:user_id] - ) - end - # Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched, # close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables. def process_commit_messages(ref) @@ -114,30 +107,24 @@ class GitPushService end def push_to_existing_branch?(ref, oldrev) - ref_parts = ref.split('/') - # Return if this is not a push to a branch (e.g. new commits) - ref_parts[1].include?('heads') && oldrev != Gitlab::Git::BLANK_SHA + Gitlab::Git.branch_ref?(ref) && oldrev != Gitlab::Git::BLANK_SHA end def push_to_new_branch?(ref, oldrev) - ref_parts = ref.split('/') - - ref_parts[1].include?('heads') && oldrev == Gitlab::Git::BLANK_SHA + Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(oldrev) end def push_remove_branch?(ref, newrev) - ref_parts = ref.split('/') - - ref_parts[1].include?('heads') && newrev == Gitlab::Git::BLANK_SHA + Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(newrev) end def push_to_branch?(ref) - ref.include?('refs/heads') + Gitlab::Git.branch_ref?(ref) end def is_default_branch?(ref) - ref == "refs/heads/#{project.default_branch}" + Gitlab::Git.branch_ref?(ref) && Gitlab::Git.ref_name(ref) == project.default_branch end def commit_user(commit) diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index c24809ad607..cd92f50b02a 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -5,13 +5,10 @@ class GitTagPushService @project, @user = project, user @push_data = create_push_data(oldrev, newrev, ref) - create_push_event + EventCreateService.new.push(project, user, @push_data) project.repository.expire_cache project.execute_hooks(@push_data.dup, :tag_push_hooks) - - if project.gitlab_ci? - project.gitlab_ci_service.async_execute(@push_data) - end + project.execute_services(@push_data.dup, :tag_push_hooks) true end @@ -19,16 +16,8 @@ class GitTagPushService private def create_push_data(oldrev, newrev, ref) - Gitlab::PushDataBuilder. - build(project, user, oldrev, newrev, ref, []) - end - - def create_push_event - Event.create!( - project: project, - action: Event::PUSHED, - data: push_data, - author_id: push_data[:user_id] - ) + data = Gitlab::PushDataBuilder.build(project, user, oldrev, newrev, ref, []) + data[:object_kind] = "tag_push" + data end end diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 755c0ef45a8..c3ca04a4343 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -1,13 +1,19 @@ module Issues class BaseService < ::IssuableBaseService - private - - def execute_hooks(issue, action = 'open') + def hook_data(issue, action) issue_data = issue.to_hook_data(current_user) issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id) issue_data[:object_attributes].merge!(url: issue_url, action: action) + issue_data + end + + private + + def execute_hooks(issue, action = 'open') + issue_data = hook_data(issue, action) issue.project.execute_hooks(issue_data, :issue_hooks) + issue.project.execute_services(issue_data, :issue_hooks) end end end diff --git a/app/services/issues/bulk_update_service.rb b/app/services/issues/bulk_update_service.rb index f72a346af6f..c7cd20b6b60 100644 --- a/app/services/issues/bulk_update_service.rb +++ b/app/services/issues/bulk_update_service.rb @@ -1,38 +1,23 @@ module Issues class BulkUpdateService < BaseService def execute - update_data = params[:update] + issues_ids = params.delete(:issues_ids).split(",") + issue_params = params - issues_ids = update_data[:issues_ids].split(",") - milestone_id = update_data[:milestone_id] - assignee_id = update_data[:assignee_id] - status = update_data[:status] - - new_state = nil - - if status.present? - if status == 'closed' - new_state = :close - else - new_state = :reopen - end - end - - opts = {} - opts[:milestone_id] = milestone_id if milestone_id.present? - opts[:assignee_id] = assignee_id if assignee_id.present? + issue_params.delete(:state_event) unless issue_params[:state_event].present? + issue_params.delete(:milestone_id) unless issue_params[:milestone_id].present? + issue_params.delete(:assignee_id) unless issue_params[:assignee_id].present? issues = Issue.where(id: issues_ids) - issues = issues.select { |issue| can?(current_user, :modify_issue, issue) } - issues.each do |issue| - issue.update_attributes(opts) - issue.send new_state if new_state + next unless can?(current_user, :modify_issue, issue) + + Issues::UpdateService.new(issue.project, current_user, issue_params).execute(issue) end { - count: issues.count, - success: !issues.count.zero? + count: issues.count, + success: !issues.count.zero? } end end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index b4199d1c800..f6e1ae6f283 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -5,13 +5,19 @@ module MergeRequests Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) end + def hook_data(merge_request, action) + hook_data = merge_request.to_hook_data(current_user) + merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id) + hook_data[:object_attributes][:url] = merge_request_url + hook_data[:object_attributes][:action] = action + hook_data + end + def execute_hooks(merge_request, action = 'open') if merge_request.project - hook_data = merge_request.to_hook_data(current_user) - merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id) - hook_data[:object_attributes][:url] = merge_request_url - hook_data[:object_attributes][:action] = action - merge_request.project.execute_hooks(hook_data, :merge_request_hooks) + merge_data = hook_data(merge_request, action) + merge_request.project.execute_hooks(merge_data, :merge_request_hooks) + merge_request.project.execute_services(merge_data, :merge_request_hooks) end end end diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 859c3f56b2b..a44b91166e8 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -16,9 +16,6 @@ module MergeRequests return build_failed(merge_request, nil) end - # Generate suggested MR title based on source branch name - merge_request.title = merge_request.source_branch.titleize.humanize - compare_result = CompareService.new.execute( current_user, merge_request.source_project, @@ -52,6 +49,15 @@ module MergeRequests merge_request.compare_failed = false end + commits = merge_request.compare_commits + if commits && commits.count == 1 + commit = commits.first + merge_request.title = commit.title + merge_request.description = commit.description.try(:strip) + else + merge_request.title = merge_request.source_branch.titleize.humanize + end + merge_request rescue Gitlab::Satellite::BranchesWithoutParent diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index a6705de61f2..cab8a1e880e 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -1,10 +1,10 @@ module MergeRequests class RefreshService < MergeRequests::BaseService def execute(oldrev, newrev, ref) - return true unless ref =~ /heads/ + return true unless Gitlab::Git.branch_ref?(ref) @oldrev, @newrev = oldrev, newrev - @branch_name = ref.gsub("refs/heads/", "") + @branch_name = Gitlab::Git.ref_name(ref) @fork_merge_requests = @project.fork_merge_requests.opened @commits = @project.repository.commits_between(oldrev, newrev) @@ -53,7 +53,7 @@ module MergeRequests if merge_request.source_branch == @branch_name || force_push? merge_request.reload_code - merge_request.mark_as_unchecked + update_merge_request(merge_request) else mr_commit_ids = merge_request.commits.map(&:id) push_commit_ids = @commits.map(&:id) @@ -61,14 +61,20 @@ module MergeRequests if matches.any? merge_request.reload_code - merge_request.mark_as_unchecked + update_merge_request(merge_request) else - merge_request.mark_as_unchecked + update_merge_request(merge_request) end end end end + def update_merge_request(merge_request) + MergeRequests::UpdateService.new( + merge_request.target_project, + @current_user, merge_status: 'unchecked').execute(merge_request) + end + # Add comment about pushing new commits to merge requests def comment_mr_with_commits merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a @@ -76,8 +82,14 @@ module MergeRequests merge_requests = filter_merge_requests(merge_requests) merge_requests.each do |merge_request| + mr_commit_ids = Set.new(merge_request.commits.map(&:id)) + + new_commits, existing_commits = @commits.partition do |commit| + mr_commit_ids.include?(commit.id) + end + Note.create_new_commits_note(merge_request, merge_request.project, - @current_user, @commits) + @current_user, new_commits, existing_commits) end end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index f64006a4edc..e969061f229 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -17,10 +17,22 @@ module Notes note.references.each do |mentioned| Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project) end + + execute_hooks(note) end end note end + + def hook_data(note) + Gitlab::NoteDataBuilder.build(note, current_user) + end + + def execute_hooks(note) + note_data = hook_data(note) + # TODO: Support Webhooks + note.project.execute_services(note_data, :note_hooks) + end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 2fc63b9f4b7..0063b7ce40c 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -268,6 +268,7 @@ class NotificationService # Also remove duplications and nil recipients def reject_muted_users(users, project = nil) users = users.to_a.compact.uniq + users = users.reject(&:blocked?) users.reject do |user| next user.notification.disabled? unless project diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 139de70114b..4fe790b98f1 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -52,13 +52,7 @@ module Projects end end - if @project.persisted? - if @project.wiki_enabled? - @project.create_wiki - end - - after_create_actions - end + after_create_actions if @project.persisted? @project rescue => ex @@ -79,6 +73,10 @@ module Projects def after_create_actions log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"") + + @project.create_wiki if @project.wiki_enabled? + + event_service.create_project(@project, current_user) system_hook_service.execute_hooks_for(@project, :create) unless @project.group diff --git a/app/services/projects/image_service.rb b/app/services/projects/image_service.rb deleted file mode 100644 index 7ca7e82c4a3..00000000000 --- a/app/services/projects/image_service.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Projects - class ImageService < BaseService - include Rails.application.routes.url_helpers - def initialize(repository, params, root_url) - @repository, @params, @root_url = repository, params.dup, root_url - end - - def execute - uploader = FileUploader.new('uploads', upload_path, accepted_images) - image = @params['markdown_img'] - - if image && correct_mime_type?(image) - alt = image.original_filename - uploader.store!(image) - link = { - 'alt' => File.basename(alt, '.*'), - 'url' => File.join(@root_url, uploader.url) - } - else - link = nil - end - end - - protected - - def upload_path - base_dir = FileUploader.generate_dir - File.join(@repository.path_with_namespace, base_dir) - end - - def accepted_images - %w(png jpg jpeg gif) - end - - def correct_mime_type?(image) - accepted_images.map{ |format| image.content_type.include? format }.any? - end - end -end diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 0be50fed7cc..f6f9aceef95 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -35,15 +35,21 @@ module Projects end def sorted(users) - users.uniq.to_a.compact.sort_by(&:username).map { |user| { username: user.username, name: user.name } } + users.uniq.to_a.compact.sort_by(&:username).map do |user| + { username: user.username, name: user.name } + end end def groups - @user.authorized_groups.sort_by(&:path).map { |group| { username: group.path, name: group.name } } + @user.authorized_groups.sort_by(&:path).map do |group| + count = group.users.count + { username: group.path, name: "#{group.name} (#{count})" } + end end def all_members - [{ username: "all", name: "Project and Group Members" }] + count = @project.team.members.flatten.count + [{ username: "all", name: "All Project and Group Members (#{count})" }] end end end diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index e39fe882cb1..3372cfc11d0 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -12,7 +12,7 @@ module Projects class TransferError < StandardError; end def execute - namespace_id = params[:namespace_id] + namespace_id = params[:new_namespace_id] namespace = Namespace.find_by(id: namespace_id) if allowed_transfer?(current_user, project, namespace) diff --git a/app/services/projects/upload_service.rb b/app/services/projects/upload_service.rb new file mode 100644 index 00000000000..a186c97628f --- /dev/null +++ b/app/services/projects/upload_service.rb @@ -0,0 +1,22 @@ +module Projects + class UploadService < BaseService + def initialize(project, file) + @project, @file = project, file + end + + def execute + return nil unless @file + + uploader = FileUploader.new(@project) + uploader.store!(@file) + + filename = uploader.image? ? uploader.file.basename : uploader.file.filename + + { + 'alt' => filename, + 'url' => uploader.secure_url, + 'is_image' => uploader.image? + } + end + end +end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 46f6e91e808..c5d0b08845b 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -41,7 +41,7 @@ class SystemHooksService path_with_namespace: model.path_with_namespace, project_id: model.id, owner_name: owner.name, - owner_email: owner.respond_to?(:email) ? owner.email : nil, + owner_email: owner.respond_to?(:email) ? owner.email : "", project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase }) when User diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb index b122b6c8658..a9691bee46e 100644 --- a/app/uploaders/attachment_uploader.rb +++ b/app/uploaders/attachment_uploader.rb @@ -3,8 +3,6 @@ class AttachmentUploader < CarrierWave::Uploader::Base storage :file - after :store, :reset_events_cache - def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end @@ -22,15 +20,7 @@ class AttachmentUploader < CarrierWave::Uploader::Base false end - def secure_url - Gitlab.config.gitlab.relative_url_root + "/files/#{model.class.to_s.underscore}/#{model.id}/#{file.filename}" - end - def file_storage? self.class.storage == CarrierWave::Storage::File end - - def reset_events_cache(file) - model.reset_events_cache if model.is_a?(User) - end end diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb new file mode 100644 index 00000000000..7cad044555b --- /dev/null +++ b/app/uploaders/avatar_uploader.rb @@ -0,0 +1,32 @@ +# encoding: utf-8 + +class AvatarUploader < CarrierWave::Uploader::Base + storage :file + + after :store, :reset_events_cache + + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + def image? + img_ext = %w(png jpg jpeg gif bmp tiff) + if file.respond_to?(:extension) + img_ext.include?(file.extension.downcase) + else + # Not all CarrierWave storages respond to :extension + ext = file.path.split('.').last.downcase + img_ext.include?(ext) + end + rescue + false + end + + def file_storage? + self.class.storage == CarrierWave::Storage::File + end + + def reset_events_cache(file) + model.reset_events_cache if model.is_a?(User) + end +end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 0fa987c93f6..f9673abbfe8 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -2,40 +2,47 @@ class FileUploader < CarrierWave::Uploader::Base storage :file - def initialize(base_dir, path = '', allowed_extensions = nil) - @base_dir = base_dir - @path = path - @allowed_extensions = allowed_extensions + attr_accessor :project, :secret + + def initialize(project, secret = self.class.generate_secret) + @project = project + @secret = secret end def base_dir - @base_dir + "uploads" end def store_dir - File.join(@base_dir, @path) + File.join(base_dir, @project.path_with_namespace, @secret) end def cache_dir - File.join(@base_dir, 'tmp', @path) + File.join(base_dir, 'tmp', @project.path_with_namespace, @secret) end - def extension_white_list - @allowed_extensions + def self.generate_secret + SecureRandom.hex end - def store!(file) - @filename = self.class.generate_filename(file) - super + def secure_url + File.join(Gitlab.config.gitlab.url, @project.path_with_namespace, "uploads", @secret, file.filename) end - def self.generate_filename(file) - original_filename = File.basename(file.original_filename, '.*') - extension = File.extname(file.original_filename) - new_filename = Digest::MD5.hexdigest(original_filename) + extension + def file_storage? + self.class.storage == CarrierWave::Storage::File end - def self.generate_dir - SecureRandom.hex(5) + def image? + img_ext = %w(png jpg jpeg gif bmp tiff) + if file.respond_to?(:extension) + img_ext.include?(file.extension.downcase) + else + # Not all CarrierWave storages respond to :extension + ext = file.path.split('.').last.downcase + img_ext.include?(ext) + end + rescue + false end end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index ae0c70a79c7..520f327f4e7 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -8,34 +8,47 @@ %fieldset %legend Features .form-group - = f.label :signup_enabled, class: 'control-label' - .col-sm-10 - = f.check_box :signup_enabled, class: 'checkbox' + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :signup_enabled do + = f.check_box :signup_enabled + Signin enabled .form-group - = f.label :signin_enabled, class: 'control-label' - .col-sm-10 - = f.check_box :signin_enabled, class: 'checkbox' + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :signin_enabled do + = f.check_box :signin_enabled + Signup enabled .form-group - = f.label :gravatar_enabled, class: 'control-label' - .col-sm-10 - = f.check_box :gravatar_enabled, class: 'checkbox' + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :gravatar_enabled do + = f.check_box :gravatar_enabled + Gravatar enabled + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :twitter_sharing_enabled do + = f.check_box :twitter_sharing_enabled, :'aria-describedby' => 'twitter_help_block' + %strong Twitter enabled + %span.help-block#twitter_help_block Show users a button to share their newly created public or internal projects on twitter %fieldset %legend Misc .form-group - = f.label :default_projects_limit, class: 'control-label' + = f.label :default_projects_limit, class: 'control-label col-sm-2' .col-sm-10 = f.number_field :default_projects_limit, class: 'form-control' .form-group - = f.label :default_branch_protection, class: 'control-label' + = f.label :default_branch_protection, class: 'control-label col-sm-2' .col-sm-10 = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control' .form-group - = f.label :home_page_url, class: 'control-label' + = f.label :home_page_url, class: 'control-label col-sm-2' .col-sm-10 - = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com' - %span.help-block We will redirect non-logged in users to this page + = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block' + %span.help-block#home_help_block We will redirect non-logged in users to this page .form-group - = f.label :sign_in_text, class: 'control-label' + = f.label :sign_in_text, class: 'control-label col-sm-2' .col-sm-10 = f.text_area :sign_in_text, class: 'form-control', rows: 4 .help-block Markdown enabled diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index f2fed51eaf8..d550278710e 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -17,6 +17,6 @@ %tr{:id => "application_#{application.id}"} %td= link_to application.name, admin_application_path(application) %td= application.redirect_uri - %td= application.access_tokens.count + %td= application.access_tokens.map(&:resource_owner_id).uniq.count %td= link_to 'Edit', edit_admin_application_path(application), class: 'btn btn-link' %td= render 'delete_form', application: application diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 32e0e4a6848..d1c586328a2 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -32,9 +32,9 @@ %span.light.pull-right = Milestone.count %p - Users who signed in during last 30 days + Active Users %span.light.pull-right - = User.where("current_sign_in_at > ?", 30.days.ago).count + = User.active.count .col-md-4 %h4 Features @@ -85,10 +85,10 @@ .light-well %h4 Projects .data - = link_to admin_projects_path do + = link_to admin_namespaces_projects_path do %h1= Project.count %hr - = link_to 'New Project', new_project_path, class: "btn btn-new" + = link_to('New Project', new_project_path, class: "btn btn-new") .col-sm-4 .light-well %h4 Users @@ -112,7 +112,7 @@ %hr - @projects.each do |project| %p - = link_to project.name_with_namespace, [:admin, project], class: 'str-truncated' + = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated' %span.light.pull-right #{time_ago_with_tooltip(project.created_at)} diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index 86a73200609..9e7751830a4 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -14,7 +14,7 @@ .form-group .col-sm-2 .col-sm-10 - .bs-callout.bs-callout-info + .alert.alert-info = render 'shared/group_tips' .form-actions = f.submit 'Create group', class: "btn btn-create" diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index d356aff6365..3040faa722b 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -12,7 +12,7 @@ Group info: %ul.well-list %li - = image_tag group_icon(@group.path), class: "avatar s60" + = image_tag group_icon(@group), class: "avatar s60" %li %span.light Name: %strong= @group.name @@ -41,7 +41,7 @@ - @projects.each do |project| %li %strong - = link_to project.name_with_namespace, [:admin, project] + = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project] %span.label.label-gray = repository_size(project) %span.pull-right.light diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 36a4a2fb4af..3a1e61d5d8d 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -1,7 +1,9 @@ .row - .col-md-3 + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left + %aside.col-md-3 .admin-filter - = form_tag admin_projects_path, method: :get, class: '' do + = form_tag admin_namespaces_projects_path, method: :get, class: '' do .form-group = label_tag :name, 'Name:' = text_field_tag :name, params[:name], class: "form-control" @@ -13,15 +15,13 @@ .form-group %strong Activity .checkbox - = label_tag :with_push, 'Not empty' - = check_box_tag :with_push, 1, params[:with_push] - - %span.light Projects with push events + = label_tag :with_push do + = check_box_tag :with_push, 1, params[:with_push] + %span Projects with push events .checkbox - = label_tag :abandoned, 'Abandoned' - = check_box_tag :abandoned, 1, params[:abandoned] - - %span.light No activity over 6 month + = label_tag :abandoned do + = check_box_tag :abandoned, 1, params[:abandoned] + %span No activity over 6 month %fieldset %strong Visibility level: @@ -36,15 +36,15 @@ %hr = hidden_field_tag :sort, params[:sort] = button_tag "Search", class: "btn submit btn-primary" - = link_to "Reset", admin_projects_path, class: "btn btn-cancel" + = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel" - .col-md-9 + %section.col-md-9 .panel.panel-default .panel-heading Projects (#{@projects.total_count}) .panel-head-actions .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'} %span.light sort: - if @sort.present? = sort_options_hash[@sort] @@ -53,29 +53,29 @@ %b.caret %ul.dropdown-menu %li - = link_to admin_projects_path(sort: sort_value_recently_created) do + = link_to admin_namespaces_projects_path(sort: sort_value_recently_created) do = sort_title_recently_created - = link_to admin_projects_path(sort: sort_value_oldest_created) do + = link_to admin_namespaces_projects_path(sort: sort_value_oldest_created) do = sort_title_oldest_created - = link_to admin_projects_path(sort: sort_value_recently_updated) do + = link_to admin_namespaces_projects_path(sort: sort_value_recently_updated) do = sort_title_recently_updated - = link_to admin_projects_path(sort: sort_value_oldest_updated) do + = link_to admin_namespaces_projects_path(sort: sort_value_oldest_updated) do = sort_title_oldest_updated - = link_to admin_projects_path(sort: sort_value_largest_repo) do + = link_to admin_namespaces_projects_path(sort: sort_value_largest_repo) do = sort_title_largest_repo - = link_to 'New Project', new_project_path, class: "btn btn-new" + = link_to 'New Project', new_project_path, class: "btn btn-sm btn-success" %ul.well-list - @projects.each do |project| %li .list-item-name %span{ class: visibility_level_color(project.visibility_level) } = visibility_level_icon(project.visibility_level) - = link_to project.name_with_namespace, [:admin, project] + = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project] .pull-right %span.label.label-gray = repository_size(project) - = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" - = link_to 'Destroy', [project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-small btn-remove" + = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Destroy', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-small btn-remove" - if @projects.blank? .nothing-here-block 0 projects matches = paginate @projects, theme: "gitlab" diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 6d536199851..1421c2ea909 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -79,11 +79,11 @@ .panel-heading Transfer project .panel-body - = form_for @project, url: transfer_admin_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f| + = form_for @project, url: transfer_admin_namespace_project_path(@project.namespace, @project), method: :put, html: { class: 'form-horizontal' } do |f| .form-group - = f.label :namespace_id, "Namespace", class: 'control-label' + = f.label :new_namespace_id, "Namespace", class: 'control-label' .col-sm-10 - = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large' + = namespace_select_tag :new_namespace_id, selected: params[:namespace_id], class: 'input-large' .form-group .col-sm-2 @@ -111,7 +111,7 @@ %small (#{@project.users.count}) .pull-right - = link_to project_team_index_path(@project), class: "btn btn-tiny" do + = link_to namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-tiny" do %i.fa.fa-pencil-square-o Manage Access %ul.well-list.team_members @@ -126,7 +126,7 @@ %span.light Owner - else %span.light= project_member.human_access - = link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-small btn-remove" do + = link_to namespace_project_team_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-small btn-remove" do %i.fa.fa-times .panel-footer = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab' diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml new file mode 100644 index 00000000000..291e48efc12 --- /dev/null +++ b/app/views/admin/services/_form.html.haml @@ -0,0 +1,81 @@ +%h3.page-title + = @service.title + +%p #{@service.description} template + += form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'form-horizontal fieldset-form' } do |f| + - if @service.errors.any? + #error_explanation + .alert.alert-danger + - @service.errors.full_messages.each do |msg| + %p= msg + - if @service.help.present? + .alert.alert-info + = preserve do + = markdown @service.help + + .form-group + = f.label :url, "Trigger", class: 'control-label' + - if @service.supported_events.length > 1 + .col-sm-10 + - if @service.supported_events.include?("push") + %div + = f.check_box :push_events, class: 'pull-left' + .prepend-left-20 + = f.label :push_events, class: 'list-label' do + %strong Push events + %p.light + This url will be triggered by a push to the repository + - if @service.supported_events.include?("tag_push") + %div + = f.check_box :tag_push_events, class: 'pull-left' + .prepend-left-20 + = f.label :tag_push_events, class: 'list-label' do + %strong Tag push events + %p.light + This url will be triggered when a new tag is pushed to the repository + - if @service.supported_events.include?("issue") + %div + = f.check_box :issues_events, class: 'pull-left' + .prepend-left-20 + = f.label :issues_events, class: 'list-label' do + %strong Issues events + %p.light + This url will be triggered when an issue is created + - if @service.supported_events.include?("merge_request") + %div + = f.check_box :merge_requests_events, class: 'pull-left' + .prepend-left-20 + = f.label :merge_requests_events, class: 'list-label' do + %strong Merge Request events + %p.light + This url will be triggered when a merge request is created + + - @service.fields.each do |field| + - name = field[:name] + - title = field[:title] || name.humanize + - value = @service.send(name) unless field[:type] == 'password' + - type = field[:type] + - placeholder = field[:placeholder] + - choices = field[:choices] + - default_choice = field[:default_choice] + - help = field[:help] + + .form-group + = f.label name, title, class: "control-label" + .col-sm-10 + - if type == 'text' + = f.text_field name, class: "form-control", placeholder: placeholder + - elsif type == 'textarea' + = f.text_area name, rows: 5, class: "form-control", placeholder: placeholder + - elsif type == 'checkbox' + = f.check_box name + - elsif type == 'select' + = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } + - elsif type == 'password' + = f.password_field name, class: 'form-control' + - if help + %span.help-block= help + + .form-actions + = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/admin/services/edit.html.haml b/app/views/admin/services/edit.html.haml new file mode 100644 index 00000000000..bcc5832792f --- /dev/null +++ b/app/views/admin/services/edit.html.haml @@ -0,0 +1 @@ += render 'form' diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml new file mode 100644 index 00000000000..0093fb97765 --- /dev/null +++ b/app/views/admin/services/index.html.haml @@ -0,0 +1,22 @@ +%h3.page-title Service templates +%p.light Service template allows you to set default values for project services + +%table.table + %thead + %tr + %th + %th Service + %th Description + %th Last edit + - @services.sort_by(&:title).each do |service| + %tr + %td + = icon("copy", class: 'clgray') + %td + = link_to edit_admin_application_settings_service_path(service.id) do + %strong= service.title + %td + = service.description + %td.light + = time_ago_in_words service.updated_at + ago diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 6e15cec467b..35e9fd5154f 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -1,5 +1,7 @@ .row - .col-md-3 + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left + %aside.col-md-3 .admin-filter %ul.nav.nav-pills.nav-stacked %li{class: "#{'active' unless params[:filter]}"} @@ -27,13 +29,13 @@ %hr = link_to 'Reset', admin_users_path, class: "btn btn-cancel" - .col-md-9 + %section.col-md-9 .panel.panel-default .panel-heading Users (#{@users.total_count}) .panel-head-actions .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-toggle.btn.btn-sm{href: '#', "data-toggle" => "dropdown"} %span.light sort: - if @sort.present? = sort_options_hash[@sort] @@ -57,7 +59,7 @@ = link_to admin_users_path(sort: sort_value_oldest_updated) do = sort_title_oldest_updated - = link_to 'New User', new_admin_user_path, class: "btn btn-new" + = link_to 'New User', new_admin_user_path, class: "btn btn-new btn-sm" %ul.well-list - @users.each do |user| %li diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 88e71aa170f..90c9f8c2f9b 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -108,45 +108,49 @@ .col-md-6 - unless @user == current_user - if @user.blocked? - .alert.alert-info - %h4 This user is blocked - %p Blocking user has the following effects: - %ul - %li User will not be able to login - %li User will not be able to access git repositories - %li User will be removed from joined projects and groups - %li Personal projects will be left - %li Owned groups will be left - %br - = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", data: { confirm: 'Are you sure?' } + .panel.panel-info + .panel-heading + This user is blocked + .panel-body + %p Blocking user has the following effects: + %ul + %li User will not be able to login + %li User will not be able to access git repositories + %li Personal projects will be left + %li Owned groups will be left + %br + = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-info", data: { confirm: 'Are you sure?' } - else - .alert.alert-warning - %h4 Block this user - %p Blocking user has the following effects: + .panel.panel-warning + .panel-heading + Block this user + .panel-body + %p Blocking user has the following effects: + %ul + %li User will not be able to login + %li User will not be able to access git repositories + %li User will be removed from joined projects and groups + %li Personal projects will be left + %li Owned groups will be left + %br + = link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-warning" + + .panel.panel-danger + .panel-heading + Remove user + .panel-body + %p Deleting a user has the following effects: %ul - %li User will not be able to login - %li User will not be able to access git repositories - %li User will be removed from joined projects and groups - %li Personal projects will be left - %li Owned groups will be left + %li All user content like authored issues, snippets, comments will be removed + - rp = @user.personal_projects.count + - unless rp.zero? + %li #{pluralize rp, 'personal project'} will be removed and cannot be restored + - if @user.solo_owned_groups.present? + %li + Next groups with all content will be removed: + %strong #{@user.solo_owned_groups.map(&:name).join(', ')} %br - = link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-remove" - - .alert.alert-danger - %h4 - Remove user - %p Deleting a user has the following effects: - %ul - %li All user content like authored issues, snippets, comments will be removed - - rp = @user.personal_projects.count - - unless rp.zero? - %li #{pluralize rp, 'personal project'} will be removed and cannot be restored - - if @user.solo_owned_groups.present? - %li - Next groups with all content will be removed: - %strong #{@user.solo_owned_groups.map(&:name).join(', ')} - %br - = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove" + = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove" #profile.tab-pane .row @@ -206,7 +210,7 @@ - tm = project.team.find_tm(@user.id) %li.project_member .list-item-name - = link_to admin_project_path(project), class: dom_class(project) do + = link_to admin_namespace_project_path(project.namespace, project), class: dom_class(project) do = project.name_with_namespace - if tm @@ -217,7 +221,7 @@ %span.light= tm.human_access - if tm.respond_to? :project - = link_to project_team_member_path(project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do + = link_to namespace_project_team_member_path(project.namespace, project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do %i.fa.fa-times #ssh-keys.tab-pane = render 'profiles/keys/key_table', admin: true diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml index fdf96dd6f56..c1fc1602d0a 100644 --- a/app/views/dashboard/_activities.html.haml +++ b/app/views/dashboard/_activities.html.haml @@ -1,9 +1,4 @@ = render "events/event_last_push", event: @last_push = render 'shared/event_filter' - -- if @events.any? - .content_list -- else - .nothing-here-block Projects activity will be displayed here - +.content_list = spinner diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml deleted file mode 100644 index ddf44270802..00000000000 --- a/app/views/dashboard/_groups.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -.panel.panel-default - .panel-heading.clearfix - .input-group - = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' - - if current_user.can_create_group? - .input-group-addon - = link_to new_group_path, class: "" do - %strong New group - %ul.well-list.dash-list - - groups.each do |group| - %li.group-row - = link_to group_path(id: group.path), class: dom_class(group) do - .dash-project-avatar - = image_tag group_icon(group.path), class: "avatar s40" - %span.group-name.filter-title - = truncate(group.name, length: 35) - %span.arrow - %i.fa.fa-angle-right - - if groups.blank? - %li - .nothing-here-block You have no groups yet. diff --git a/app/views/dashboard/_project.html.haml b/app/views/dashboard/_project.html.haml deleted file mode 100644 index f0fb2c1881b..00000000000 --- a/app/views/dashboard/_project.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -= link_to project_path(project), class: dom_class(project) do - .dash-project-avatar - = project_icon(project.to_param, alt: '', class: 'avatar project-avatar s40') - .dash-project-access-icon - = visibility_level_icon(project.visibility_level) - %span.str-truncated - %span.namespace-name - - if project.namespace - = project.namespace.human_name - \/ - %span.project-name.filter-title - = project.name - %span.arrow - %i.fa.fa-angle-right diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml index 0596738342f..3634b2bfd7b 100644 --- a/app/views/dashboard/_projects.html.haml +++ b/app/views/dashboard/_projects.html.haml @@ -1,25 +1,10 @@ .panel.panel-default .panel-heading.clearfix .input-group - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' - if current_user.can_create_project? .input-group-addon.dash-new-project = link_to new_project_path do %strong New project - %ul.well-list.dash-list - - projects.each do |project| - %li.project-row - = render "project", project: project - - - if projects.blank? - %li - .nothing-here-block There are no projects here. - - if @projects_count > @projects_limit - %li.bottom - %span.light - #{@projects_limit} of #{pluralize(@projects_count, 'project')} displayed. - .pull-right - = link_to projects_dashboard_path do - Show all - %i.fa.fa-angle-right + = render 'shared/projects_list', projects: @projects, projects_limit: 20 diff --git a/app/views/dashboard/_projects_filter.html.haml b/app/views/dashboard/_projects_filter.html.haml deleted file mode 100644 index 7b5d46072e3..00000000000 --- a/app/views/dashboard/_projects_filter.html.haml +++ /dev/null @@ -1,100 +0,0 @@ -.dash-projects-filters.append-bottom-20 - .pull-left.append-right-20 - %ul.nav.nav-pills.nav-compact - = nav_tab :scope, nil do - = link_to projects_dashboard_filter_path(scope: nil) do - All - = nav_tab :scope, 'personal' do - = link_to projects_dashboard_filter_path(scope: 'personal') do - Personal - = nav_tab :scope, 'joined' do - = link_to projects_dashboard_filter_path(scope: 'joined') do - Joined - = nav_tab :scope, 'owned' do - = link_to projects_dashboard_filter_path(scope: 'owned') do - Owned - - .dropdown.inline.append-right-10 - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-globe - %span.light Visibility: - - if params[:visibility_level].present? - = visibility_level_label(params[:visibility_level].to_i) - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to projects_dashboard_filter_path(visibility_level: nil) do - Any - - Gitlab::VisibilityLevel.values.each do |level| - %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' } - = link_to projects_dashboard_filter_path(visibility_level: level) do - = visibility_level_icon(level) - = visibility_level_label(level) - - - if @groups.present? - .dropdown.inline.append-right-10 - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-group - %span.light Group: - - if params[:group].present? - = Group.find_by(name: params[:group]).name - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to projects_dashboard_filter_path(group: nil) do - Any - - @groups.each do |group| - %li{ class: (group.name == params[:group]) ? 'active' : 'light' } - = link_to projects_dashboard_filter_path(group: group.name) do - = group.name - %small.pull-right - = group.projects.count - - - - - if @tags.present? - .dropdown.inline.append-right-10 - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-tags - %span.light Tags: - - if params[:tag].present? - = params[:tag] - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to projects_dashboard_filter_path(tag: nil) do - Any - - - @tags.each do |tag| - %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' } - = link_to projects_dashboard_filter_path(tag: tag.name) do - %i.fa.fa-tag - = tag.name - - .pull-right - .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %span.light sort: - - if @sort.present? - = sort_options_hash[@sort] - - else - = sort_title_recently_created - %b.caret - %ul.dropdown-menu - %li - = link_to projects_dashboard_filter_path(sort: sort_value_recently_created) do - = sort_title_recently_created - = link_to projects_dashboard_filter_path(sort: sort_value_oldest_created) do - = sort_title_oldest_created - = link_to projects_dashboard_filter_path(sort: sort_value_recently_updated) do - = sort_title_recently_updated - = link_to projects_dashboard_filter_path(sort: sort_value_oldest_updated) do - = sort_title_oldest_updated - = link_to projects_dashboard_filter_path(sort: sort_value_name) do - = sort_title_name diff --git a/app/views/dashboard/_sidebar.html.haml b/app/views/dashboard/_sidebar.html.haml index a980f495427..78f695be916 100644 --- a/app/views/dashboard/_sidebar.html.haml +++ b/app/views/dashboard/_sidebar.html.haml @@ -1,18 +1,3 @@ -%ul.nav.nav-tabs.dash-sidebar-tabs - %li.active - = link_to '#projects', 'data-toggle' => 'tab', id: 'sidebar-projects-tab' do - Projects - %span.badge= @projects_count - %li - = link_to '#groups', 'data-toggle' => 'tab', id: 'sidebar-groups-tab' do - Groups - %span.badge= @groups.count - -.tab-content - .tab-pane.active#projects - = render "projects", projects: @projects - .tab-pane#groups - = render "groups", groups: @groups - += render "dashboard/projects", projects: @projects .prepend-top-20 = render 'shared/promo' diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml index f78ce69ef9e..4e7d6639727 100644 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/_zero_authorized_projects.html.haml @@ -1,3 +1,4 @@ +- publicish_project_count = Project.publicish(current_user).count %h3.page-title Welcome to GitLab! %p.light Self hosted Git management application. %hr @@ -17,7 +18,8 @@ - if current_user.can_create_project? .link_holder = link_to new_project_path, class: "btn btn-new" do - New project » + %i.fa.fa-plus + New Project - if current_user.can_create_group? %hr @@ -31,9 +33,10 @@ Groups are the best way to manage projects and members. .link_holder = link_to new_group_path, class: "btn btn-new" do - New group » + %i.fa.fa-plus + New Group --if @publicish_project_count > 0 +-if publicish_project_count > 0 %hr %div .dashboard-intro-icon @@ -41,10 +44,10 @@ .dashboard-intro-text %p.slead There are - %strong= @publicish_project_count + %strong= publicish_project_count public projects on this server. %br Public projects are an easy way to allow everyone to have read-only access. .link_holder = link_to trending_explore_projects_path, class: "btn btn-new" do - Browse public projects » + Browse public projects diff --git a/app/views/profiles/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index e9ffca8faf4..f7df5352512 100644 --- a/app/views/profiles/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -1,12 +1,12 @@ %h3.page-title - Group membership + Group Membership - if current_user.can_create_group? %span.pull-right = link_to new_group_path, class: "btn btn-new" do %i.fa.fa-plus New Group %p.light - Group members have access to all a group's projects + Group members have access to all group projects. %hr .panel.panel-default .panel-heading @@ -23,10 +23,11 @@ Settings - if can?(current_user, :destroy, user_group) - = link_to leave_profile_group_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-small btn btn-grouped", title: 'Remove user from group' do + = link_to leave_dashboard_group_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-small btn btn-grouped", title: 'Remove user from group' do %i.fa.fa-sign-out Leave + = image_tag group_icon(group), class: "avatar s40 avatar-tile" = link_to group, class: 'group-name' do %strong= group.name diff --git a/app/views/dashboard/milestones/_issue.html.haml b/app/views/dashboard/milestones/_issue.html.haml new file mode 100644 index 00000000000..f689b9698eb --- /dev/null +++ b/app/views/dashboard/milestones/_issue.html.haml @@ -0,0 +1,10 @@ +%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid } + %span.milestone-row + - project = issue.project + %strong #{project.name_with_namespace} · + = link_to [project.namespace.becomes(Namespace), project, issue] do + %span.cgray ##{issue.iid} + = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title + .pull-right.assignee-icon + - if issue.assignee + = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/_issues.html.haml b/app/views/dashboard/milestones/_issues.html.haml new file mode 100644 index 00000000000..9f350b772bd --- /dev/null +++ b/app/views/dashboard/milestones/_issues.html.haml @@ -0,0 +1,6 @@ +.panel.panel-default + .panel-heading= title + %ul{ class: "well-list issues-sortable-list" } + - if issues + - issues.each do |issue| + = render 'issue', issue: issue diff --git a/app/views/dashboard/milestones/_merge_request.html.haml b/app/views/dashboard/milestones/_merge_request.html.haml new file mode 100644 index 00000000000..8f5c4cce529 --- /dev/null +++ b/app/views/dashboard/milestones/_merge_request.html.haml @@ -0,0 +1,10 @@ +%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid } + %span.milestone-row + - project = merge_request.project + %strong #{project.name_with_namespace} · + = link_to [project.namespace.becomes(Namespace), project, merge_request] do + %span.cgray ##{merge_request.iid} + = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title + .pull-right.assignee-icon + - if merge_request.assignee + = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/_merge_requests.html.haml b/app/views/dashboard/milestones/_merge_requests.html.haml new file mode 100644 index 00000000000..50057e2c636 --- /dev/null +++ b/app/views/dashboard/milestones/_merge_requests.html.haml @@ -0,0 +1,6 @@ +.panel.panel-default + .panel-heading= title + %ul{ class: "well-list merge_requests-sortable-list" } + - if merge_requests + - merge_requests.each do |merge_request| + = render 'merge_request', merge_request: merge_request diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml new file mode 100644 index 00000000000..caf3b685864 --- /dev/null +++ b/app/views/dashboard/milestones/index.html.haml @@ -0,0 +1,38 @@ +%h3.page-title + Milestones + %span.pull-right #{@dashboard_milestones.count} milestones + +%p.light + List all milestones from all projects you have access to. + +%hr + += render 'shared/milestones_filter' +.milestones + .panel.panel-default + %ul.well-list + - if @dashboard_milestones.blank? + %li + .nothing-here-block No milestones to show + - else + - @dashboard_milestones.each do |milestone| + %li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) } + %h4 + = link_to_gfm truncate(milestone.title, length: 100), dashboard_milestone_path(milestone.safe_title, title: milestone.title) + %div + %div + = link_to dashboard_milestone_path(milestone.safe_title, title: milestone.title) do + = pluralize milestone.issue_count, 'Issue' + + = link_to dashboard_milestone_path(milestone.safe_title, title: milestone.title) do + = pluralize milestone.merge_requests_count, 'Merge Request' + + %span.light #{milestone.percent_complete}% complete + = milestone_progress_bar(milestone) + %div + %br + - milestone.milestones.each do |milestone| + = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) do + %span.label.label-default + = milestone.project.name_with_namespace + = paginate @dashboard_milestones, theme: "gitlab" diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml new file mode 100644 index 00000000000..57cce9ab749 --- /dev/null +++ b/app/views/dashboard/milestones/show.html.haml @@ -0,0 +1,81 @@ +%h4.page-title + .issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" } + - if @dashboard_milestone.closed? + Closed + - else + Open + Milestone #{@dashboard_milestone.title} + +%hr +- if (@dashboard_milestone.total_items_count == @dashboard_milestone.closed_items_count) && @dashboard_milestone.active? + .alert.alert-success + %span All issues for this milestone are closed. You may close the milestone now. + +.description +%table.table + %thead + %tr + %th Project + %th Open issues + %th State + %th Due date + - @dashboard_milestone.milestones.each do |milestone| + %tr + %td + = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) + %td + = milestone.issues.opened.count + %td + - if milestone.closed? + Closed + - else + Open + %td + = milestone.expires_at + +.context + %p.lead + Progress: + #{@dashboard_milestone.closed_items_count} closed + – + #{@dashboard_milestone.open_items_count} open + = milestone_progress_bar(@dashboard_milestone) + +%ul.nav.nav-tabs + %li.active + = link_to '#tab-issues', 'data-toggle' => 'tab' do + Issues + %span.badge= @dashboard_milestone.issue_count + %li + = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do + Merge Requests + %span.badge= @dashboard_milestone.merge_requests_count + %li + = link_to '#tab-participants', 'data-toggle' => 'tab' do + Participants + %span.badge= @dashboard_milestone.participants.count + +.tab-content + .tab-pane.active#tab-issues + .row + .col-md-6 + = render 'issues', title: "Open", issues: @dashboard_milestone.opened_issues + .col-md-6 + = render 'issues', title: "Closed", issues: @dashboard_milestone.closed_issues + + .tab-pane#tab-merge-requests + .row + .col-md-6 + = render 'merge_requests', title: "Open", merge_requests: @dashboard_milestone.opened_merge_requests + .col-md-6 + = render 'merge_requests', title: "Closed", merge_requests: @dashboard_milestone.closed_merge_requests + + .tab-pane#tab-participants + %ul.bordered-list + - @dashboard_milestone.participants.each do |user| + %li + = link_to user, title: user.name, class: "darken" do + = image_tag avatar_icon(user.email, 32), class: "avatar s32" + %strong= truncate(user.name, lenght: 40) + %br + %small.cgray= user.username diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml deleted file mode 100644 index dba3025b3cc..00000000000 --- a/app/views/dashboard/projects.html.haml +++ /dev/null @@ -1,56 +0,0 @@ -%h3.page-title - My Projects - -%p.light - All projects you have access to are listed here. Public projects are not included here unless you are a member -%hr -.side-filters - = render "projects_filter" -.dash-projects - %ul.bordered-list.my-projects.top-list - - @projects.each do |project| - %li.my-project-row - %h4.project-title - .pull-left - = project_icon(project.to_param, alt: '', class: 'avatar project-avatar s60') - .project-access-icon - = visibility_level_icon(project.visibility_level) - = link_to project_path(project), class: dom_class(project) do - = project.name_with_namespace - - - if project.forked_from_project - - %small - %i.fa.fa-code-fork - Forked from: - = link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project) - - - if current_user.can_leave_project?(project) - .pull-right - = link_to leave_project_team_members_path(project), data: { confirm: "Leave project?"}, method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do - %i.fa.fa-sign-out - Leave - - .project-info - .pull-right - - if project.archived? - %span.label - %i.fa.fa-archive - Archived - - project.tags.each do |tag| - %span.label.label-info - %i.fa.fa-tag - = tag.name - - if project.description.present? - %p= truncate project.description, length: 100 - .last-activity - %span.light Last activity: - %span.date= project_last_activity(project) - - - - if @projects.blank? - %li - .nothing-here-block There are no projects here. - .bottom - = paginate @projects, theme: "gitlab" - diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml new file mode 100644 index 00000000000..94de6092563 --- /dev/null +++ b/app/views/dashboard/projects/starred.html.haml @@ -0,0 +1,23 @@ +- if @projects.any? + .dashboard.row + %section.activities.col-md-8 + = render 'dashboard/activities' + %aside.col-md-4 + .panel.panel-default + .panel-heading.clearfix + .input-group + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' + - if current_user.can_create_project? + .input-group-addon.dash-new-project + = link_to new_project_path do + %strong New project + + = render 'shared/projects_list', projects: @projects, + projects_limit: 20, stars: true, avatar: false + + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left + +- else + %h3 You dont have starred projects yet + %p.slead Visit project page and press on star icon and it will appear on this page. diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml index 10951af6a09..fa8946011b7 100644 --- a/app/views/dashboard/show.html.haml +++ b/app/views/dashboard/show.html.haml @@ -1,12 +1,11 @@ -- if @has_authorized_projects +- if @projects.any? .dashboard.row %section.activities.col-md-8 = render 'activities' - %aside.side.col-md-4.left.responsive-side + %aside.col-md-4 = render 'sidebar' - - .fixed.sidebar-expand-button.hidden-lg.hidden-md - %i.fa.fa-list.fa-2x + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left - else = render "zero_authorized_projects" diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index c07e409d583..d3e37f7494c 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -1,7 +1,3 @@ = render 'devise/shared/signup_box' -.clearfix.prepend-top-20 - = render 'devise/shared/sign_in_link' - %p - %span.light Did not receive confirmation email? - = link_to "Send again", new_confirmation_path(resource_name)
\ No newline at end of file += render 'devise/shared/sign_in_link'
\ No newline at end of file diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index ab9085f0ba7..54a39726771 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -2,11 +2,11 @@ = f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus" = f.password_field :password, class: "form-control bottom", placeholder: "Password" - if devise_mapping.rememberable? - .remember-me - %label.checkbox.remember_me{for: "user_remember_me"} + .remember-me.checkbox + %label{for: "user_remember_me"} = f.check_box :remember_me %span Remember me - .pull-right - = link_to "Forgot your password?", new_password_path(resource_name) + .pull-right + = link_to "Forgot your password?", new_password_path(resource_name) %div = f.submit "Sign in", class: "btn btn-save" diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 6d8415613d1..89e4e229ac0 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,15 +1,18 @@ %div - = render 'devise/shared/signin_box' + - if signin_enabled? || ldap_enabled? + = render 'devise/shared/signin_box' + -# Omniauth fits between signin/ldap signin and signup and does not have a surrounding box - if Gitlab.config.omniauth.enabled && devise_mapping.omniauthable? - .prepend-top-20 - = render 'devise/shared/oauth_box' + .clearfix.prepend-top-20 + = render 'devise/shared/omniauth_box' - - if signup_enabled? + -# Signup only makes sense if you can also sign-in + - if signin_enabled? && signup_enabled? .prepend-top-20 = render 'devise/shared/signup_box' -.clearfix.prepend-top-20 - %p - %span.light Did not receive confirmation email? - = link_to "Send again", new_confirmation_path(resource_name) + -# Show a message if none of the mechanisms above are enabled + - if !signin_enabled? && !ldap_enabled? && !(Gitlab.config.omniauth.enabled && devise_mapping.omniauthable?) + %div + No authentication methods configured. diff --git a/app/views/devise/shared/_oauth_box.html.haml b/app/views/devise/shared/_oauth_box.html.haml deleted file mode 100644 index c2e1373de30..00000000000 --- a/app/views/devise/shared/_oauth_box.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -- providers = additional_providers -- if providers.present? - .login-box{:'data-no-turbolink' => 'data-no-turbolink'} - %span Sign in with - - providers.each do |provider| - %span - - if default_providers.include?(provider) - = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) - - else - = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml new file mode 100644 index 00000000000..4cd1c303b22 --- /dev/null +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -0,0 +1,10 @@ +%p + %span.light + Sign in with + - providers = additional_providers + - providers.each do |provider| + %span.light + - if default_providers.include?(provider) + = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) + - else + = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index 70587329033..8faa6398a60 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -1,6 +1,10 @@ .login-box - .login-heading - %h3 Sign in + - if signup_enabled? + .login-heading + %h3 Existing user? Sign in + - else + .login-heading + %h3 Sign in .login-body - if ldap_enabled? %ul.nav.nav-tabs @@ -20,6 +24,3 @@ - elsif signin_enabled? = render 'devise/sessions/new_base' - - else - %div - No authentication methods configured. diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 8a6dc19ab64..dcf60c90430 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -1,6 +1,10 @@ .login-box - .login-heading - %h3 Sign up + - if signin_enabled? + .login-heading + %h3 New user? Create an account + - else + .login-heading + %h3 Create an account .login-body = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| .devise-errors @@ -15,3 +19,8 @@ = f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true %div = f.submit "Sign up", class: "btn-create btn" + +.clearfix.prepend-top-20 + %p + %span.light Did not receive confirmation email? + = link_to "Send again", new_confirmation_path(resource_name)
\ No newline at end of file diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml index f0c34def145..c86ce9ae651 100644 --- a/app/views/events/_commit.html.haml +++ b/app/views/events/_commit.html.haml @@ -1,5 +1,5 @@ %li.commit .commit-row-title - = link_to truncate_sha(commit[:id]), project_commit_path(project, commit[:id]), class: "commit_short_id", alt: '' + = link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: '' = gfm event_commit_title(commit[:message]), project diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index c7976ba564f..02b1dec753c 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -3,12 +3,14 @@ .event-item-timestamp #{time_ago_with_tooltip(event.created_at)} - = cache event do + = cache [event, current_user] do = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' - if event.push? = render "events/event/push", event: event - - elsif event.note? + - elsif event.commented? = render "events/event/note", event: event + - elsif event.created_project? + = render "events/event/created_project", event: event - else = render "events/event/common", event: event
\ No newline at end of file diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml index 4c9a39bcc27..cb40aa9970b 100644 --- a/app/views/events/_event_last_push.html.haml +++ b/app/views/events/_event_last_push.html.haml @@ -2,7 +2,7 @@ .event-last-push .event-last-push-text %span You pushed to - = link_to project_commits_path(event.project, event.ref_name) do + = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do %strong= event.ref_name at %strong= link_to_project event.project diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml index 2b63519edac..0ffd2aa0b98 100644 --- a/app/views/events/_event_push.atom.haml +++ b/app/views/events/_event_push.atom.haml @@ -2,7 +2,7 @@ - event.commits.first(15).each do |commit| %p %strong= commit[:author][:name] - = link_to "(##{truncate_sha(commit[:id])})", project_commit_path(event.project, id: commit[:id]) + = link_to "(##{truncate_sha(commit[:id])})", namespace_project_commit_path(event.project.namespace, event.project, id: commit[:id]) %i at = commit[:timestamp].to_time.to_s(:short) diff --git a/app/views/events/_events.html.haml b/app/views/events/_events.html.haml index 3d62d478869..68c19df092d 100644 --- a/app/views/events/_events.html.haml +++ b/app/views/events/_events.html.haml @@ -1 +1 @@ -= render @events += render partial: 'events/event', collection: @events diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index a9d3adf41df..a39e62e9dac 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -1,15 +1,17 @@ .event-title %span.author_name= link_to_author event - %span.event_label{class: event.action_name}= event_action_name(event) + %span.event_label{class: event.action_name} + = event_action_name(event) + - if event.target - %strong= link_to "##{event.target_iid}", [event.project, event.target] - - else - %strong= gfm event.target_title - at + %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] + at + - if event.project = link_to_project event.project - else = event.project_name + - if event.target.respond_to?(:title) .event-body .event-note diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml new file mode 100644 index 00000000000..3c7153d235f --- /dev/null +++ b/app/views/events/event/_created_project.html.haml @@ -0,0 +1,27 @@ +.event-title + %span.author_name= link_to_author event + %span.event_label{class: event.action_name} + = event_action_name(event) + + - if event.project + = link_to_project event.project + - else + = event.project_name + +- if current_user == event.author && !event.project.private? && twitter_sharing_enabled? + .event-body + .event-note + .md + %p + Congratulations! Why not share your accomplishment with the world? + + %a.twitter-share-button{ | + href: "https://twitter.com/share", | + "data-url" => event.project.web_url, | + "data-text" => "I just created a new project in GitLab! GitLab is version control on your server.", | + "data-size" => "medium", | + "data-related" => "gitlab", | + "data-hashtags" => "gitlab", | + "data-count" => "none"} + Tweet + %script{src: "//platform.twitter.com/widgets.js"}
\ No newline at end of file diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml index 6ec8e54fba5..4ef18c09060 100644 --- a/app/views/events/event/_note.html.haml +++ b/app/views/events/event/_note.html.haml @@ -1,6 +1,10 @@ .event-title %span.author_name= link_to_author event - %span.event_label commented on #{event_note_title_html(event)} at + %span.event_label + = event.action_name + = event_note_title_html(event) + at + - if event.project = link_to_project event.project - else @@ -14,9 +18,9 @@ - note = event.target - if note.attachment.url - if note.attachment.image? - = link_to note.attachment.secure_url, target: '_blank' do - = image_tag note.attachment.secure_url, class: 'note-image-attach' + = link_to note.attachment.url, target: '_blank' do + = image_tag note.attachment.url, class: 'note-image-attach' - else - = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do + = link_to note.attachment.url, target: "_blank", class: 'note-file-attach' do %i.fa.fa-paperclip = note.attachment_identifier diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index b912b5e092f..489138887ae 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -1,10 +1,10 @@ .event-title %span.author_name= link_to_author event - %span.event_label.pushed #{event.push_action_name} #{event.ref_type} + %span.event_label.pushed #{event.action_name} #{event.ref_type} - if event.rm_ref? %strong= event.ref_name - else - = link_to project_commits_path(event.project, event.ref_name) do + = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do %strong= event.ref_name at = link_to_project event.project @@ -21,5 +21,5 @@ %li.commits-stat - if event.commits_count > 2 %span ... and #{event.commits_count - 2} more commits. - = link_to project_compare_path(event.project, from: event.commit_from, to: event.commit_to) do + = link_to namespace_project_compare_path(event.project.namespace, event.project, from: event.commit_from, to: event.commit_to) do %strong Compare → #{truncate_sha(event.commit_from)}...#{truncate_sha(event.commit_to)} diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index 5cf514927af..2ea6cb18655 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -9,7 +9,7 @@ .pull-right .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light sort: - if @sort.present? = sort_options_hash[@sort] diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml new file mode 100644 index 00000000000..b3963a9d901 --- /dev/null +++ b/app/views/explore/projects/_filter.html.haml @@ -0,0 +1,67 @@ +.pull-left + = form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f| + .form-group + = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search" + .form-group + = button_tag 'Search', class: "btn btn-primary wide" + +.pull-right.hidden-sm.hidden-xs + - if current_user + .dropdown.inline.append-right-10 + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.fa.fa-globe + %span.light Visibility: + - if params[:visibility_level].present? + = visibility_level_label(params[:visibility_level].to_i) + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to explore_projects_filter_path(visibility_level: nil) do + Any + - Gitlab::VisibilityLevel.values.each do |level| + %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' } + = link_to explore_projects_filter_path(visibility_level: level) do + = visibility_level_icon(level) + = visibility_level_label(level) + + - if @tags.present? + .dropdown.inline.append-right-10 + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.fa.fa-tags + %span.light Tags: + - if params[:tag].present? + = params[:tag] + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to explore_projects_filter_path(tag: nil) do + Any + + - @tags.each do |tag| + %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' } + = link_to explore_projects_filter_path(tag: tag.name) do + %i.fa.fa-tag + = tag.name + + .dropdown.inline + %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} + %span.light sort: + - if @sort.present? + = sort_options_hash[@sort] + - else + = sort_title_recently_created + %b.caret + %ul.dropdown-menu + %li + = link_to explore_projects_filter_path(sort: sort_value_recently_created) do + = sort_title_recently_created + = link_to explore_projects_filter_path(sort: sort_value_oldest_created) do + = sort_title_oldest_created + = link_to explore_projects_filter_path(sort: sort_value_recently_updated) do + = sort_title_recently_updated + = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do + = sort_title_oldest_updated diff --git a/app/views/explore/projects/_project.html.haml b/app/views/explore/projects/_project.html.haml index ffbddbae4d6..d65fb529373 100644 --- a/app/views/explore/projects/_project.html.haml +++ b/app/views/explore/projects/_project.html.haml @@ -2,12 +2,10 @@ %h4.project-title .project-access-icon = visibility_level_icon(project.visibility_level) - = link_to project.name_with_namespace, project - - - if current_page?(starred_explore_projects_path) - %strong.pull-right - %i.fa.fa-star - = pluralize project.star_count, 'star' + = link_to project.name_with_namespace, [project.namespace.becomes(Namespace), project] + %span.pull-right + %i.fa.fa-star + = project.star_count .project-info - if project.description.present? @@ -16,11 +14,11 @@ .repo-info - unless project.empty_repo? - = link_to pluralize(project.repository.round_commit_count, 'commit'), project_commits_path(project, project.default_branch) + = link_to pluralize(project.repository.round_commit_count, 'commit'), namespace_project_commits_path(project.namespace, project, project.default_branch) · - = link_to pluralize(project.repository.branch_names.count, 'branch'), project_branches_path(project) + = link_to pluralize(project.repository.branch_names.count, 'branch'), namespace_project_branches_path(project.namespace, project) · - = link_to pluralize(project.repository.tag_names.count, 'tag'), project_tags_path(project) + = link_to pluralize(project.repository.tag_names.count, 'tag'), namespace_project_tags_path(project.namespace, project) - else %i.fa.fa-exclamation-triangle Empty repository diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index 02d02912791..5086b58cd03 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,30 +1,5 @@ .clearfix - .pull-left - = form_tag explore_projects_path, method: :get, class: 'form-inline form-tiny' do |f| - .form-group - = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search" - .form-group - = button_tag 'Search', class: "btn btn-primary wide" - - .pull-right - .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %span.light sort: - - if @sort.present? - = sort_options_hash[@sort] - - else - = sort_title_recently_created - %b.caret - %ul.dropdown-menu - %li - = link_to explore_projects_path(sort: sort_value_recently_created) do - = sort_title_recently_created - = link_to explore_projects_path(sort: sort_value_oldest_created) do - = sort_title_oldest_created - = link_to explore_projects_path(sort: sort_value_recently_updated) do - = sort_title_recently_updated - = link_to explore_projects_path(sort: sort_value_oldest_updated) do - = sort_title_oldest_updated + = render 'filter' %hr .public-projects diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index a2f1d28a275..6f53e125c47 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -1,23 +1,10 @@ .panel.panel-default - .panel-heading - Projects (#{projects.count}) - - if can? current_user, :create_projects, @group - .panel-head-actions - = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do - %i.fa.fa-plus - New project - %ul.well-list - - if projects.blank? - .nothing-here-block This group has no projects yet - - projects.each do |project| - %li.project-row - = link_to project_path(project), class: dom_class(project) do - .dash-project-avatar - = project_icon(project.to_param, alt: '', class: 'avatar s40') - .dash-project-access-icon - = visibility_level_icon(project.visibility_level) - %span.str-truncated - %span.project-name - = project.name - %span.arrow - %i.fa.fa-angle-right + .panel-heading.clearfix + .input-group + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' + - if can? current_user, :create_projects, @group + .input-group-addon.dash-new-project + = link_to new_project_path(namespace_id: @group.id) do + %strong New project + + = render 'shared/projects_list', projects: @projects, projects_limit: 20 diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index c4eb00e8925..838290e4aca 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -12,7 +12,7 @@ .form-group .col-sm-2 .col-sm-10 - = image_tag group_icon(@group.to_param), alt: '', class: 'avatar group-avatar s160' + = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160' %p.light - if @group.avatar? You can change your group avatar here diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index d05016d9c3f..6267006f63f 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -8,17 +8,21 @@ %span.cgray= user.username - if user == current_user %span.label.label-success It's you + - if user.blocked? + %label.label.label-danger + %strong Blocked - if show_roles %span.pull-right %strong= member.human_access - if show_controls - if can?(current_user, :modify, member) - = link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do + = button_tag class: "btn-tiny btn js-toggle-button", + title: 'Edit access level', type: 'button' do %i.fa.fa-pencil-square-o - if can?(current_user, :destroy, member) - if current_user == member.user - = link_to leave_profile_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do + = link_to leave_dashboard_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do %i.fa.fa-minus.fa-inverse - else = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do diff --git a/app/views/groups/members.html.haml b/app/views/groups/members.html.haml index d2ebcdab7e1..688c22e9624 100644 --- a/app/views/groups/members.html.haml +++ b/app/views/groups/members.html.haml @@ -17,7 +17,7 @@ - if current_user && current_user.can?(:manage_group, @group) .pull-right - = link_to '#', class: 'btn btn-new js-toggle-button' do + = button_tag class: 'btn btn-new js-toggle-button', type: 'button' do Add members %i.fa.fa-chevron-down diff --git a/app/views/groups/milestones/_issue.html.haml b/app/views/groups/milestones/_issue.html.haml index c95c2e89670..27d0c62df8c 100644 --- a/app/views/groups/milestones/_issue.html.haml +++ b/app/views/groups/milestones/_issue.html.haml @@ -2,9 +2,9 @@ %span.milestone-row - project = issue.project %strong #{project.name} · - = link_to [project, issue] do + = link_to [project.namespace.becomes(Namespace), project, issue] do %span.cgray ##{issue.iid} - = link_to_gfm issue.title, [project, issue], title: issue.title + = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title .pull-right.assignee-icon - if issue.assignee = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16" diff --git a/app/views/groups/milestones/_merge_request.html.haml b/app/views/groups/milestones/_merge_request.html.haml index e0c903bfdb2..b2d2097dfab 100644 --- a/app/views/groups/milestones/_merge_request.html.haml +++ b/app/views/groups/milestones/_merge_request.html.haml @@ -2,9 +2,9 @@ %span.milestone-row - project = merge_request.project %strong #{project.name} · - = link_to [project, merge_request] do + = link_to [project.namespace.becomes(Namespace), project, merge_request] do %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [project, merge_request], title: merge_request.title + = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title .pull-right.assignee-icon - if merge_request.assignee = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16" diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index 7f0b2832cac..9febaab04a7 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -36,11 +36,11 @@ = pluralize milestone.merge_requests_count, 'Merge Request' %span.light #{milestone.percent_complete}% complete - .progress.progress-info - .progress-bar{style: "width: #{milestone.percent_complete}%;"} + = milestone_progress_bar(milestone) %div %br - - milestone.projects.each do |project| - %span.label.label-default - = project.name + - milestone.milestones.each do |milestone| + = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) do + %span.label.label-default + = milestone.project.name = paginate @group_milestones, theme: "gitlab" diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 7bcac56c37b..dd2d84499ba 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -28,7 +28,7 @@ - @group_milestone.milestones.each do |milestone| %tr %td - = link_to "#{milestone.project.name}", project_milestone_path(milestone.project, milestone) + = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) %td = milestone.issues.opened.count %td @@ -45,8 +45,7 @@ #{@group_milestone.closed_items_count} closed – #{@group_milestone.open_items_count} open - .progress.progress-info - .progress-bar{style: "width: #{@group_milestone.percent_complete}%;"} + = milestone_progress_bar(@group_milestone) %ul.nav.nav-tabs %li.active diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 40c81e8cd5b..c95347b3a55 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -4,7 +4,7 @@ projects: - if can? current_user, :manage_group, @group .panel-head-actions - = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do + = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do %i.fa.fa-plus New Project %ul.well-list @@ -16,8 +16,8 @@ %span.label.label-gray = repository_size(project) .pull-right - = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" - = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Members', namespace_project_team_index_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-small" = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-small btn-remove" - if @projects.blank? .nothing-here-block This group has no projects yet diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index f2e591c1939..25efe973d4f 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,6 +1,6 @@ .dashboard %div - = image_tag group_icon(@group.path), class: "avatar group-avatar s90" + = image_tag group_icon(@group), class: "avatar group-avatar s90" .clearfix %h2 = @group.name @@ -9,14 +9,13 @@ = escaped_autolink(@group.description) %hr .row - %section.activities.col-md-8.hidden-sm.hidden-xs + %section.activities.col-md-8 - if current_user = render "events/event_last_push", event: @last_push = render 'shared/event_filter' - - if @events.any? - .content_list - - else - .nothing-here-block Project activity will be displayed here + .content_list = spinner %aside.side.col-md-4 = render "projects", projects: @projects + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 7b8193abfdf..af39dfeac5b 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -42,3 +42,9 @@ %li Use = link_to 'shortcuts', '#', onclick: 'Shortcuts.showHelp(event)' + %li + Get a support + = link_to 'subscription', 'https://about.gitlab.com/pricing/' + %li + = link_to 'Compare', 'https://about.gitlab.com/features/#compare' + GitLab editions diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml new file mode 100644 index 00000000000..58de5b7c869 --- /dev/null +++ b/app/views/help/ui.html.haml @@ -0,0 +1,208 @@ +- lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare." + +.gitlab-ui-dev-kit + %h1 GitLab UI development kit + %p.light + Use page inspector in your browser to check element classes and structure + of examples below. + %hr + %ul + %li + = link_to 'Blocks', '#blocks' + %li + = link_to 'Lists', '#lists' + %li + = link_to 'Tables', '#tables' + %li + = link_to 'Buttons', '#buttons' + %li + = link_to 'Panels', '#panels' + %li + = link_to 'Alerts', '#alerts' + %li + = link_to 'Forms', '#forms' + %li + = link_to 'Markdown', '#markdown' + + %h2#blocks Blocks + + %h3 + %code .well + + + .well + %h4 Something + = lorem + + + %h2#lists Lists + + %h3 + %code .well-list + %ul.well-list + %li + One item + %li + One item + %li + One item + + %h3 + %code .panel .well-list + + .panel.panel-default + .panel-heading My list + %ul.well-list + %li + One item + %li + One item + %li + One item + + %h3 + %code .bordered-list + %ul.bordered-list + %li + One item + %li + One item + %li + One item + + + + %h2#tables Tables + + .example + %table.table + %thead + %tr + %th # + %th First Name + %th Last Name + %th Username + %tbody + %tr + %td 1 + %td Mark + %td Otto + %td @mdo + %tr + %td 2 + %td Jacob + %td Thornton + %td @fat + %tr + %td 3 + %td Larry + %td the Bird + %td @twitter + + + %h2#buttons Buttons + + .example + %button.btn.btn-default{:type => "button"} Default + %button.btn.btn-primary{:type => "button"} Primary + %button.btn.btn-success{:type => "button"} Success + %button.btn.btn-info{:type => "button"} Info + %button.btn.btn-warning{:type => "button"} Warning + %button.btn.btn-danger{:type => "button"} Danger + %button.btn.btn-link{:type => "button"} Link + + %h2#panels Panels + + .row + .col-md-6 + .panel.panel-success + .panel-heading Success + .panel-body + = lorem + .panel.panel-primary + .panel-heading Primary + .panel-body + = lorem + .panel.panel-info + .panel-heading Info + .panel-body + = lorem + .col-md-6 + .panel.panel-warning + .panel-heading Warning + .panel-body + = lorem + .panel.panel-danger + .panel-heading Danger + .panel-body + = lorem + + %h2#alert Alerts + + .row + .col-md-6 + .alert.alert-success + = lorem + .alert.alert-primary + = lorem + .alert.alert-info + = lorem + .col-md-6 + .alert.alert-warning + = lorem + .alert.alert-danger + = lorem + + %h2#forms Forms + + %h3 + %code form.horizontal-form + + %form.form-horizontal + .form-group + %label.col-sm-2.control-label{:for => "inputEmail3"} Email + .col-sm-10 + %input#inputEmail3.form-control{:placeholder => "Email", :type => "email"}/ + .form-group + %label.col-sm-2.control-label{:for => "inputPassword3"} Password + .col-sm-10 + %input#inputPassword3.form-control{:placeholder => "Password", :type => "password"}/ + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + %label + %input{:type => "checkbox"}/ + Remember me + .form-group + .col-sm-offset-2.col-sm-10 + %button.btn.btn-default{:type => "submit"} Sign in + + %h3 + %code form + + %form + .form-group + %label{:for => "exampleInputEmail1"} Email address + %input#exampleInputEmail1.form-control{:placeholder => "Enter email", :type => "email"}/ + .form-group + %label{:for => "exampleInputPassword1"} Password + %input#exampleInputPassword1.form-control{:placeholder => "Password", :type => "password"}/ + .checkbox + %label + %input{:type => "checkbox"}/ + Remember me + %button.btn.btn-default{:type => "submit"} Sign in + + %h2#markdown Markdown + %h3 + %code .md or .wiki and others + + Markdown rendering has a bit different css and presented in next UI elements: + + %ul + %li comment + %li issue, merge request description + %li wiki page + %li help page + + You can check how markdown rendered at #{link_to 'Markdown help page', help_page_path("markdown", "markdown")}. diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml index cd4c9fbf360..8d10722628f 100644 --- a/app/views/import/base/create.js.haml +++ b/app/views/import/base/create.js.haml @@ -10,9 +10,16 @@ target_field.append("/" + project_name) target_field.data("project_name", project_name) target_field.find('input').prop("value", origin_namespace) +- elsif @access_denied + :plain + job = $("tr#repo_#{@repo_id}") + job.find(".import-actions").html("<p class='alert alert-danger'>Access denied! Please verify you can add deploy keys to this repository.</p>"") - else :plain job = $("tr#repo_#{@repo_id}") job.attr("id", "project_#{@project.id}") + target_field = job.find(".import-target") + target_field.empty() + target_field.append('<strong>#{link_to @project.path_with_namespace, [@project.namespace.becomes(Namespace), @project]}</strong>') $("table.import-jobs tbody").prepend(job) job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started") diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml new file mode 100644 index 00000000000..9da3c920c62 --- /dev/null +++ b/app/views/import/bitbucket/status.html.haml @@ -0,0 +1,46 @@ +%h3.page-title + %i.fa.fa-bitbucket + Import projects from Bitbucket + +%p.light + Select projects you want to import. +%hr +%p + = button_tag 'Import all projects', class: "btn btn-success js-import-all" + +%table.table.import-jobs + %thead + %tr + %th From Bitbucket + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name + + - @repos.each do |repo| + %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} + %td + = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" + %td.import-target + = "#{repo["owner"]}/#{repo["slug"]}" + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" + +:coffeescript + $ -> + new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}") diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 84d9903fe15..9c4d91013ec 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -1,6 +1,6 @@ %h3.page-title %i.fa.fa-github - Import repositories from GitHub.com + Import projects from GitHub %p.light Select projects you want to import. @@ -17,20 +17,25 @@ %tbody - @already_added_projects.each do |project| %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td= project.import_source %td - %strong= link_to project.path_with_namespace, project + = link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' - %span.cgreen + %span %i.fa.fa-check done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started - else = project.human_import_status_name - @repos.each do |repo| %tr{id: "repo_#{repo.id}"} - %td= repo.full_name + %td + = link_to repo.full_name, "https://github.com/#{repo.full_name}", target: "_blank" %td.import-target = repo.full_name %td.import-actions.job-status diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index d1e48dfad20..e809643d8d4 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -1,6 +1,6 @@ %h3.page-title - %i.fa.fa-github - Import repositories from GitLab.com + %i.fa.fa-heart + Import projects from GitLab.com %p.light Select projects you want to import. @@ -12,25 +12,30 @@ %thead %tr %th From GitLab.com - %th To GitLab private instance + %th To this GitLab instance %th Status %tbody - @already_added_projects.each do |project| %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td= project.import_source %td - %strong= link_to project.path_with_namespace, project + = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' - %span.cgreen + %span %i.fa.fa-check done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started - else = project.human_import_status_name - @repos.each do |repo| %tr{id: "repo_#{repo["id"]}"} - %td= repo["path_with_namespace"] + %td + = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank" %td.import-target = repo["path_with_namespace"] %td.import-actions.job-status diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml new file mode 100644 index 00000000000..645241a6c69 --- /dev/null +++ b/app/views/import/gitorious/status.html.haml @@ -0,0 +1,46 @@ +%h3.page-title + %i.icon-gitorious.icon-gitorious-big + Import projects from Gitorious.org + +%p.light + Select projects you want to import. +%hr +%p + = button_tag 'Import all projects', class: "btn btn-success js-import-all" + +%table.table.import-jobs + %thead + %tr + %th From Gitorious.org + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name + + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = link_to repo.full_name, "https://gitorious.org/#{repo.full_name}", target: "_blank" + %td.import-target + = repo.full_name + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" + +:coffeescript + $ -> + new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}") diff --git a/app/views/layouts/_collapse_button.html.haml b/app/views/layouts/_collapse_button.html.haml index b3b338b55bb..2ed51d87ca1 100644 --- a/app/views/layouts/_collapse_button.html.haml +++ b/app/views/layouts/_collapse_button.html.haml @@ -1,4 +1,4 @@ - if nav_menu_collapsed? - = link_to icon('angle-right'), '#', class: 'toggle-nav-collapse' + = link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close" - else - = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse' + = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close" diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index a6900f4a04b..d12145651af 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -1,12 +1,5 @@ %head %meta{charset: "utf-8"} - - -# Go repository retrieval support - -# Need to be the fist thing in the head - -# Since Go is using an XML parser to process HTML5 - -# https://github.com/gitlabhq/gitlabhq/pull/5958#issuecomment-45397555 - - if controller_name == 'projects' && action_name == 'show' - %meta{name: "go-import", content: "#{@project.web_url_without_protocol} git #{@project.web_url}.git"} %meta{content: "GitLab Community Edition", name: "description"} %title @@ -30,6 +23,6 @@ = auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed" - if @project && !@project.new_record? - if current_controller?(:tree, :commits) - = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}") + = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}") - if current_controller?(:issues) - = auto_discovery_link_tag(:atom, project_issues_url(@project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues") + = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues") diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 77bfe4f996e..fc8a487ece7 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -3,7 +3,7 @@ .container %div.app_logo = link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do - %h1 GITLAB + = brand_header_logo %h1.title= title %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} @@ -42,7 +42,7 @@ = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do %i.fa.fa-sign-out %li.hidden-xs - = link_to current_user, class: "profile-pic", id: 'profile-pic' do + = link_to current_user, class: "profile-pic has_bottom_tooltip", id: 'profile-pic', 'data-original-title' => 'Your profile' do = image_tag avatar_icon(current_user.email, 60), alt: 'User activity' = render 'shared/outdated_browser' diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 353f7ce34f1..3c58f10e759 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -1,3 +1,3 @@ :javascript - GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project, type: @noteable.class, type_id: params[:id])}" + GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(@project.namespace, @project, type: @noteable.class, type_id: params[:id])}" GitLab.GfmAutoComplete.setup(); diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 98a3d2278a3..422966cdc55 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -17,3 +17,7 @@ = yield = yield :embedded_scripts + +:coffeescript + $('.page-sidebar-collapsed .nav-sidebar a').tooltip placement: "right" + diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml index e912fea2aee..3d6d2bfc00a 100644 --- a/app/views/layouts/_public_head_panel.html.haml +++ b/app/views/layouts/_public_head_panel.html.haml @@ -2,10 +2,8 @@ .navbar-inner .container %div.app_logo - %span.separator = link_to explore_root_path, class: "home" do - %h1 GITLAB - %span.separator + = brand_header_logo %h1.title= title %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} @@ -14,7 +12,7 @@ - unless current_controller?('sessions') .pull-right.hidden-xs - = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new' + = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new append-right-10' .navbar-collapse.collapse %ul.nav.navbar-nav diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index dc8652cb145..ab84e87c300 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Admin area" - %body{class: "#{app_theme} #{theme_type} admin", :'data-page' => body_data_page} - = render "layouts/head_panel", title: "Admin area" + %body{class: "#{app_theme} admin", :'data-page' => body_data_page} + = render "layouts/head_panel", title: link_to("Admin area", admin_root_path) = render 'layouts/page', sidebar: 'layouts/nav/admin' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index e5420a13605..6bd8ac4adb8 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Dashboard" - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page } - = render "layouts/head_panel", title: "Dashboard" + %body{class: "#{app_theme} application", :'data-page' => body_data_page } + = render "layouts/head_panel", title: link_to("Dashboard", root_path) = render 'layouts/page', sidebar: 'layouts/nav/dashboard' diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index e7d875173e6..e51fd4cb820 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Error" - %body{class: "#{app_theme} #{theme_type} application"} + %body{class: "#{app_theme} application"} = render "layouts/head_panel", title: "" if current_user .container.navless-container = render "layouts/flash" diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml index 9813d846542..2bd0b8d85c9 100644 --- a/app/views/layouts/explore.html.haml +++ b/app/views/layouts/explore.html.haml @@ -2,12 +2,12 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: page_title - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} application", :'data-page' => body_data_page} = render "layouts/broadcast" - if current_user - = render "layouts/head_panel", title: page_title + = render "layouts/head_panel", title: link_to(page_title, explore_root_path) - else - = render "layouts/public_head_panel", title: page_title + = render "layouts/public_head_panel", title: link_to(page_title, explore_root_path) .container.navless-container .content .explore-title diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 98edcf3a140..f4a6bee15f6 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: group_head_title - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} - = render "layouts/head_panel", title: @group.name + %body{class: "#{app_theme} application", :'data-page' => body_data_page} + = render "layouts/head_panel", title: link_to(@group.name, group_path(@group)) = render 'layouts/page', sidebar: 'layouts/nav/group' diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 4813a4f16f5..2f38d596c65 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -5,7 +5,7 @@ %span Overview = nav_link(controller: :projects) do - = link_to admin_projects_path, title: 'Projects' do + = link_to admin_namespaces_projects_path, title: 'Projects' do %i.fa.fa-cube %span Projects @@ -46,6 +46,12 @@ %span Applications + = nav_link(controller: :services) do + = link_to admin_application_settings_services_path, title: 'Service Templates' do + %i.fa.fa-copy + %span + Service Templates + = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = link_to admin_application_settings_path, title: 'Settings' do %i.fa.fa-cogs diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 48c7c999427..e4f630c6a18 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -3,12 +3,22 @@ = link_to root_path, title: 'Home', class: 'shortcuts-activity' do %i.fa.fa-dashboard %span - Activity - = nav_link(path: 'dashboard#projects') do - = link_to projects_dashboard_path, title: 'Projects', class: 'shortcuts-projects' do - %i.fa.fa-cube + Your Projects + = nav_link(path: 'projects#starred') do + = link_to starred_dashboard_projects_path, title: 'Starred Projects' do + %i.fa.fa-star %span - Projects + Starred Projects + = nav_link(controller: :groups) do + = link_to dashboard_groups_path, title: 'Groups' do + %i.fa.fa-group + %span + Groups + = nav_link(controller: :milestones) do + = link_to dashboard_milestones_path, title: 'Milestones' do + %i.fa.fa-clock-o + %span + Milestones = nav_link(path: 'dashboard#issues') do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do %i.fa.fa-exclamation-circle @@ -26,4 +36,3 @@ %i.fa.fa-question-circle %span Help - diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 0914d2a167a..d88e862829d 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -43,11 +43,6 @@ %i.fa.fa-image %span Design - = nav_link(controller: :groups) do - = link_to profile_groups_path, title: 'Groups' do - %i.fa.fa-group - %span - Groups = nav_link(path: 'profiles#history') do = link_to history_profile_path, title: 'History' do %i.fa.fa-history diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 8d572ddcd10..d340ab1796a 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -2,16 +2,11 @@ - if @project_settings_nav = nav_link do = link_to project_path(@project), title: 'Back to project', class: "" do - %i.fa.fa-angle-left + %i.fa.fa-caret-square-o-left %span Back to project - = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do - = link_to edit_project_path(@project), title: 'Settings', class: "stat-tab tab no-highlight" do - %i.fa.fa-cogs - %span - Settings - %i.fa.fa-angle-down + %li.separate-item = render 'projects/settings_nav' @@ -23,34 +18,40 @@ Project - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do - = link_to project_tree_path(@project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree' do + = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree' do %i.fa.fa-files-o %span Files - if project_nav_tab? :commits = nav_link(controller: %w(commit commits compare repositories tags branches)) do - = link_to project_commits_path(@project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do + = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do %i.fa.fa-history %span Commits - if project_nav_tab? :network = nav_link(controller: %w(network)) do - = link_to project_network_path(@project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do + = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do %i.fa.fa-code-fork %span Network - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do - = link_to project_graph_path(@project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs' do + = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs' do %i.fa.fa-area-chart %span Graphs + = nav_link(controller: :milestones) do + = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do + %i.fa.fa-clock-o + %span + Milestones + - if project_nav_tab? :issues - = nav_link(controller: %w(issues milestones labels)) do + = nav_link(controller: :issues) do = link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do %i.fa.fa-exclamation-circle %span @@ -60,22 +61,28 @@ - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do + = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %i.fa.fa-tasks %span Merge Requests %span.count.merge_counter= @project.merge_requests.opened.count + = nav_link(controller: :labels) do + = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do + %i.fa.fa-tags + %span + Labels + - if project_nav_tab? :wiki = nav_link(controller: :wikis) do - = link_to project_wiki_path(@project, :home), title: 'Wiki', class: 'shortcuts-wiki' do + = link_to namespace_project_wiki_path(@project.namespace, @project, :home), title: 'Wiki', class: 'shortcuts-wiki' do %i.fa.fa-book %span Wiki - if project_nav_tab? :snippets = nav_link(controller: :snippets) do - = link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do + = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do %i.fa.fa-file-text-o %span Snippets @@ -86,4 +93,3 @@ %i.fa.fa-cogs %span Settings - %i.fa.fa-angle-down diff --git a/app/views/layouts/navless.html.haml b/app/views/layouts/navless.html.haml index 730f3d09277..4d0278251a6 100644 --- a/app/views/layouts/navless.html.haml +++ b/app/views/layouts/navless.html.haml @@ -1,9 +1,9 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @title - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} application", :'data-page' => body_data_page} = render "layouts/broadcast" - = render "layouts/head_panel", title: @title + = render "layouts/head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title .container.navless-container .content = render "layouts/flash" diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index a722db2f32c..7eec93abdf6 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -16,6 +16,18 @@ font-size:small; color:#777 } + pre.commit-message { + white-space: pre-wrap; + } + .file-stats a { + text-decoration: none; + } + .file-stats .new-file { + color: #090; + } + .file-stats .deleted-file { + color: #B00; + } #{add_email_highlight_css} %body %div.content @@ -27,5 +39,5 @@ - if @target_url #{link_to "View it on GitLab", @target_url} = email_action @target_url - - if @project - You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, project_url(@project)} project team. + - if @project && !@disable_footer + You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, namespace_project_url(@project.namespace, @project)} project team. diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index 89d816061e2..2b5be7fc372 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Profile" - %body{class: "#{app_theme} #{theme_type} profile", :'data-page' => body_data_page} - = render "layouts/head_panel", title: "Profile" + %body{class: "#{app_theme} profile", :'data-page' => body_data_page} + = render "layouts/head_panel", title: link_to("Profile", profile_path) = render 'layouts/page', sidebar: 'layouts/nav/profile' diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml index d2c9c2a991c..0a0039dec16 100644 --- a/app/views/layouts/project_settings.html.haml +++ b/app/views/layouts/project_settings.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @project.name_with_namespace - %body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } + %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } = render "layouts/head_panel", title: project_title(@project) = render "layouts/init_auto_complete" - @project_settings_nav = true diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml index c44a40c9c12..dde0964f47f 100644 --- a/app/views/layouts/projects.html.haml +++ b/app/views/layouts/projects.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: project_head_title - %body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } + %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } = render "layouts/head_panel", title: project_title(@project) = render "layouts/init_auto_complete" = render 'layouts/page', sidebar: 'layouts/nav/project' diff --git a/app/views/layouts/public_group.html.haml b/app/views/layouts/public_group.html.haml index ae3d2bd8a89..b9b1d03e08e 100644 --- a/app/views/layouts/public_group.html.haml +++ b/app/views/layouts/public_group.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: group_head_title - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} - = render "layouts/public_head_panel", title: "group: #{@group.name}" + %body{class: "#{app_theme} application", :'data-page' => body_data_page} + = render "layouts/public_head_panel", title: link_to(@group.name, group_path(@group)) = render 'layouts/page', sidebar: 'layouts/nav/group' diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml index 027e9a53139..04fa7c84e73 100644 --- a/app/views/layouts/public_projects.html.haml +++ b/app/views/layouts/public_projects.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @project.name_with_namespace - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} application", :'data-page' => body_data_page} = render "layouts/public_head_panel", title: project_title(@project) = render 'layouts/page', sidebar: 'layouts/nav/project' diff --git a/app/views/layouts/public_users.html.haml b/app/views/layouts/public_users.html.haml index 37767df33d2..71c16bd1684 100644 --- a/app/views/layouts/public_users.html.haml +++ b/app/views/layouts/public_users.html.haml @@ -1,6 +1,6 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @title - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} - = render "layouts/public_head_panel", title: @title + %body{class: "#{app_theme} application", :'data-page' => body_data_page} + = render "layouts/public_head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title = render 'layouts/page' diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml index 6d001e7ee1c..f9d8db06e10 100644 --- a/app/views/layouts/search.html.haml +++ b/app/views/layouts/search.html.haml @@ -1,9 +1,9 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Search" - %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} application", :'data-page' => body_data_page} = render "layouts/broadcast" - = render "layouts/head_panel", title: "Search" + = render "layouts/head_panel", title: link_to("Search", search_path) .container.navless-container .content = render "layouts/flash" diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml index 5272dfa0ede..778a78acf56 100644 --- a/app/views/notify/_note_message.html.haml +++ b/app/views/notify/_note_message.html.haml @@ -1,2 +1,2 @@ %div - = markdown(@note.note) + = replace_image_links_with_base64(markdown(@note.note), @note.project) diff --git a/app/views/notify/_reassigned_issuable_email.text.erb b/app/views/notify/_reassigned_issuable_email.text.erb index 817d030c362..855d37429d9 100644 --- a/app/views/notify/_reassigned_issuable_email.text.erb +++ b/app/views/notify/_reassigned_issuable_email.text.erb @@ -1,6 +1,6 @@ Reassigned <%= issuable.class.model_name.human.titleize %> <%= issuable.iid %> -<%= url_for([issuable.project, issuable, {only_path: false}]) %> +<%= url_for([issuable.project.namespace.becomes(Namespace), issuable.project, issuable, {only_path: false}]) %> Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee -%> to <%= "#{issuable.assignee_id ? issuable.assignee_name : 'Unassigned'}" %> diff --git a/app/views/notify/closed_issue_email.text.haml b/app/views/notify/closed_issue_email.text.haml index 49f160a0d5f..ac703b31edd 100644 --- a/app/views/notify/closed_issue_email.text.haml +++ b/app/views/notify/closed_issue_email.text.haml @@ -1,3 +1,3 @@ = "Issue was closed by #{@updated_by.name}" -Issue ##{@issue.iid}: #{project_issue_url(@issue.project, @issue)} +Issue ##{@issue.iid}: #{namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)} diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml index d6b76e906c5..59db86b08bc 100644 --- a/app/views/notify/closed_merge_request_email.text.haml +++ b/app/views/notify/closed_merge_request_email.text.haml @@ -1,6 +1,6 @@ = "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}" -Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} +Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)} = merge_path_description(@merge_request, 'to') diff --git a/app/views/notify/issue_status_changed_email.text.erb b/app/views/notify/issue_status_changed_email.text.erb index 4200881f7e8..e6ab3fcde77 100644 --- a/app/views/notify/issue_status_changed_email.text.erb +++ b/app/views/notify/issue_status_changed_email.text.erb @@ -1,4 +1,4 @@ Issue was <%= @issue_status %> by <%= @updated_by.name %> -Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> +Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %> diff --git a/app/views/notify/merge_request_status_email.text.haml b/app/views/notify/merge_request_status_email.text.haml index 8750bf86e2c..b96dd0fd8ab 100644 --- a/app/views/notify/merge_request_status_email.text.haml +++ b/app/views/notify/merge_request_status_email.text.haml @@ -1,6 +1,6 @@ = "Merge Request ##{@merge_request.iid} was #{@mr_status} by #{@updated_by.name}" -Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} +Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)} = merge_path_description(@merge_request, 'to') diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml index 360da60bc3f..9db75bdb19e 100644 --- a/app/views/notify/merged_merge_request_email.text.haml +++ b/app/views/notify/merged_merge_request_email.text.haml @@ -1,6 +1,6 @@ = "Merge Request ##{@merge_request.iid} was merged" -Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} +Merge Request Url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)} = merge_path_description(@merge_request, 'to') diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index f2f8eee18c4..03cbee94608 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,5 +1,5 @@ -if @issue.description - = markdown(@issue.description) + = replace_image_links_with_base64(markdown(@issue.description), @issue.project) - if @issue.assignee_id.present? %p diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb index d36f54eb1ca..0cc62935498 100644 --- a/app/views/notify/new_issue_email.text.erb +++ b/app/views/notify/new_issue_email.text.erb @@ -1,5 +1,5 @@ New Issue was created. -Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> +Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %> Author: <%= @issue.author_name %> Asignee: <%= @issue.assignee_name %> diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index f02d5111b22..729a7bb505d 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -6,4 +6,4 @@ Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} -if @merge_request.description - = markdown(@merge_request.description) + = replace_image_links_with_base64(markdown(@merge_request.description), @merge_request.project) diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb index 16be4bb619f..f08039ad045 100644 --- a/app/views/notify/new_merge_request_email.text.erb +++ b/app/views/notify/new_merge_request_email.text.erb @@ -1,6 +1,6 @@ New Merge Request #<%= @merge_request.iid %> -<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> +<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)) %> <%= merge_path_description(@merge_request, 'to') %> Author: <%= @merge_request.author_name %> diff --git a/app/views/notify/note_commit_email.text.erb b/app/views/notify/note_commit_email.text.erb index aab8e5cfb6c..aaeaf5fdf73 100644 --- a/app/views/notify/note_commit_email.text.erb +++ b/app/views/notify/note_commit_email.text.erb @@ -1,6 +1,6 @@ New comment for Commit <%= @commit.short_id %> -<%= url_for(project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")) %> +<%= url_for(namespace_project_commit_url(@note.project.namespace, @note.project, id: @commit.id, anchor: "note_#{@note.id}")) %> Author: <%= @note.author_name %> diff --git a/app/views/notify/note_issue_email.text.erb b/app/views/notify/note_issue_email.text.erb index 8a61f54a337..e33cbcd70f2 100644 --- a/app/views/notify/note_issue_email.text.erb +++ b/app/views/notify/note_issue_email.text.erb @@ -1,6 +1,6 @@ New comment for Issue <%= @issue.iid %> -<%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %> +<%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue, anchor: "note_#{@note.id}")) %> Author: <%= @note.author_name %> diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb index 79e72ca16c6..1d1411992a6 100644 --- a/app/views/notify/note_merge_request_email.text.erb +++ b/app/views/notify/note_merge_request_email.text.erb @@ -1,6 +1,6 @@ New comment for Merge Request <%= @merge_request.iid %> -<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %> +<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %> <%= @note.author_name %> diff --git a/app/views/notify/project_access_granted_email.html.haml b/app/views/notify/project_access_granted_email.html.haml index 4596205f39b..dfc30a2d360 100644 --- a/app/views/notify/project_access_granted_email.html.haml +++ b/app/views/notify/project_access_granted_email.html.haml @@ -1,5 +1,5 @@ %p = "You have been granted #{@project_member.human_access} access to project" %p - = link_to project_url(@project) do + = link_to namespace_project_url(@project.namespace, @project) do = @project.name_with_namespace diff --git a/app/views/notify/project_access_granted_email.text.erb b/app/views/notify/project_access_granted_email.text.erb index de24feb802f..68eb1611ba7 100644 --- a/app/views/notify/project_access_granted_email.text.erb +++ b/app/views/notify/project_access_granted_email.text.erb @@ -1,4 +1,4 @@ You have been granted <%= @project_member.human_access %> access to project <%= @project.name_with_namespace %> -<%= url_for(project_url(@project)) %> +<%= url_for(namespace_project_url(@project.namespace, @project)) %> diff --git a/app/views/notify/project_was_moved_email.html.haml b/app/views/notify/project_was_moved_email.html.haml index fe248584e55..f53de2de287 100644 --- a/app/views/notify/project_was_moved_email.html.haml +++ b/app/views/notify/project_was_moved_email.html.haml @@ -2,7 +2,7 @@ Project was moved to another location %p The project is now located under - = link_to project_url(@project) do + = link_to namespace_project_url(@project.namespace, @project) do = @project.name_with_namespace %p To update the remote url in your local repository run (for ssh): diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb index 664148fb3ba..b3f18b35a4d 100644 --- a/app/views/notify/project_was_moved_email.text.erb +++ b/app/views/notify/project_was_moved_email.text.erb @@ -1,7 +1,7 @@ Project was moved to another location The project is now located under -<%= project_url(@project) %> +<%= namespace_project_url(@project.namespace, @project) %> To update the remote url in your local repository run (for ssh): diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index b6fe445867c..039b92df2be 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -1,30 +1,66 @@ -%h3 #{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, project_url(@project)} +%h3 #{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)} -%h4 Commits: +- if @reverse_compare + %p + %strong WARNING: + The push did not contain any new commits, but force pushed to delete the commits and changes below. + +%h4 + = @reverse_compare ? "Deleted commits:" : "Commits:" %ul - @commits.each do |commit| %li - %strong #{link_to commit.short_id, project_commit_url(@project, commit)} + %strong #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)} %div %span by #{commit.author_name} %i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} - %pre #{commit.safe_message} + %pre.commit-message + = commit.safe_message + +%h4 #{pluralize @diffs.count, "changed file"}: + +%ul + - @diffs.each_with_index do |diff, i| + %li.file-stats + %a{href: "#{@target_url if @disable_diffs}#diff-#{i}" } + - if diff.deleted_file + %span.deleted-file + − + = diff.old_path + - elsif diff.renamed_file + = diff.old_path + → + = diff.new_path + - elsif diff.new_file + %span.new-file + + + = diff.new_path + - else + = diff.new_path -%h4 Changes: -- @diffs.each do |diff| - %li - %strong - - if diff.old_path == diff.new_path - = diff.new_path - - elsif diff.new_path && diff.old_path - #{diff.old_path} → #{diff.new_path} - - else - = diff.new_path || diff.old_path - %hr - %pre - = color_email_diff(diff.diff) - %br +- unless @disable_diffs + %h4 Changes: + - @diffs.each_with_index do |diff, i| + %li{id: "diff-#{i}"} + %a{href: @target_url + "#diff-#{i}"} + - if diff.deleted_file + %strong + = diff.old_path + deleted + - elsif diff.renamed_file + %strong + = diff.old_path + → + %strong + = diff.new_path + - else + %strong + = diff.new_path + %hr + %pre + = color_email_diff(diff.diff) + %br - if @compare.timeout %h5 Huge diff. To prevent performance issues changes are hidden diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml index 6f5f9eda2c5..8d67a42234e 100644 --- a/app/views/notify/repository_push_email.text.haml +++ b/app/views/notify/repository_push_email.text.haml @@ -1,25 +1,47 @@ -#{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, project_url(@project)} - +#{@author.name} pushed to #{@branch} at #{@project.name_with_namespace} \ -Commits: +\ +- if @reverse_compare + WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below. + \ + \ += @reverse_compare ? "Deleted commits:" : "Commits:" - @commits.each do |commit| - #{link_to commit.short_id, project_commit_url(@project, commit)} by #{commit.author_name} + #{commit.short_id} by #{commit.author_name} at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} #{commit.safe_message} \- - - - - \ \ -Changes: +#{pluralize @diffs.count, "changed file"}: +\ - @diffs.each do |diff| - \ - \===================================== - - if diff.old_path == diff.new_path - = diff.new_path - - elsif diff.new_path && diff.old_path - #{diff.old_path} → #{diff.new_path} + - if diff.deleted_file + \- − #{diff.old_path} + - elsif diff.renamed_file + \- #{diff.old_path} → #{diff.new_path} + - elsif diff.new_file + \- + #{diff.new_path} - else - = diff.new_path || diff.old_path - \===================================== - != diff.diff -\ + \- #{diff.new_path} +- unless @disable_diffs + \ + \ + Changes: + - @diffs.each do |diff| + \ + \===================================== + - if diff.deleted_file + #{diff.old_path} deleted + - elsif diff.renamed_file + #{diff.old_path} → #{diff.new_path} + - else + = diff.new_path + \===================================== + != diff.diff - if @compare.timeout + \ + \ Huge diff. To prevent performance issues it was hidden +\ +\ +View it on GitLab: #{@target_url} diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 53a50f6796b..6bafcb56551 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -1,5 +1,5 @@ %h3.page-title - Account settings + Account Settings %p.light You can change your username and private token here. - if current_user.ldap_user? @@ -10,7 +10,7 @@ .account-page %fieldset.update-token %legend - Private token + Reset Private token %div = form_for @user, url: reset_private_token_profile_path, method: :put do |f| .data @@ -25,7 +25,7 @@ - if current_user.private_token = text_field_tag "token", current_user.private_token, class: "form-control" %div - = f.submit 'Reset', data: { confirm: "Are you sure?" }, class: "btn btn-primary btn-build-token" + = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-primary btn-build-token" - else %span You don`t have one yet. Click generate to fix it. = f.submit 'Generate', class: "btn success btn-build-token" @@ -43,7 +43,7 @@ - if show_profile_username_tab? %fieldset.update-username %legend - Username + Change Username = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| %p Changing your username will change path to all personal projects! @@ -57,7 +57,7 @@ %p.light = user_url(@user) %div - = f.submit 'Save username', class: "btn btn-save" + = f.submit 'Save username', class: "btn btn-warning" - if show_profile_remove_tab? %fieldset.remove-account diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml index cb24e4a3dde..c8c522e9812 100644 --- a/app/views/profiles/applications.html.haml +++ b/app/views/profiles/applications.html.haml @@ -1,5 +1,7 @@ %h3.page-title - OAuth2 + Application Settings +%p.light + OAuth2 protocol settings below. %fieldset.oauth-applications %legend Your applications @@ -36,12 +38,12 @@ %th Scope %th %tbody - - @authorized_tokens.each do |token| - - application = token.application - %tr{:id => "application_#{application.id}"} - %td= application.name + - @authorized_apps.each do |app| + - token = app.authorized_tokens.order('created_at desc').first + %tr{:id => "application_#{app.id}"} + %td= app.name %td= token.created_at %td= token.scopes - %td= render 'doorkeeper/authorized_applications/delete_form', application: application + %td= render 'doorkeeper/authorized_applications/delete_form', application: app - else %p.light You dont have any authorized applications diff --git a/app/views/profiles/design.html.haml b/app/views/profiles/design.html.haml index 0d8075b7d43..cc00d08d03b 100644 --- a/app/views/profiles/design.html.haml +++ b/app/views/profiles/design.html.haml @@ -1,7 +1,7 @@ %h3.page-title - My appearance settings + Design Settings %p.light - Appearance settings saved to your profile and available across all devices + Appearance settings will be saved to your profile and made available across all devices. %hr = form_for @user, url: profile_path, remote: true, method: :put do |f| @@ -33,6 +33,11 @@ .prev.violet = f.radio_button :theme_id, 5 Violet + + = label_tag do + .prev.blue + = f.radio_button :theme_id, 6 + Blue %br .clearfix diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml index 0b30e772336..3bbad6fdf7b 100644 --- a/app/views/profiles/emails/index.html.haml +++ b/app/views/profiles/emails/index.html.haml @@ -1,5 +1,5 @@ %h3.page-title - My email addresses + Email Settings %p.light Your %b Primary Email @@ -34,4 +34,4 @@ .col-sm-10 = f.text_field :email, class: 'form-control' .form-actions - = f.submit 'Add', class: 'btn btn-create' + = f.submit 'Add email address', class: 'btn btn-create' diff --git a/app/views/profiles/history.html.haml b/app/views/profiles/history.html.haml index 3951c47b5f2..9cafe03b8b3 100644 --- a/app/views/profiles/history.html.haml +++ b/app/views/profiles/history.html.haml @@ -1,7 +1,7 @@ %h3.page-title - Account history + My Account History %p.light - All events created by your account are listed here + All events created by your account are listed below. %hr .profile_history = render @events diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml index 809953960bb..965d5e032f9 100644 --- a/app/views/profiles/keys/index.html.haml +++ b/app/views/profiles/keys/index.html.haml @@ -1,12 +1,12 @@ %h3.page-title - My SSH keys (#{@keys.count}) + SSH Keys Settings .pull-right = link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new" %p.light - SSH keys allow you to establish a secure connection between your computer and GitLab + My SSH keys: #{@keys.count} %br Before you can add an SSH key you need to - = link_to "generate it", help_page_path("ssh", "ssh") + = link_to "generate it.", help_page_path("ssh", "README") %hr = render 'key_table' diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 28bc5a426ac..6cf5c81c19e 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -1,10 +1,9 @@ %h3.page-title - Notifications settings + Notifications Settings %p.light These are your global notification settings. %hr - = form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications form-horizontal global-notifications-form' } do |f| -if @user.errors.any? %div.alert.alert-danger @@ -51,7 +50,7 @@ %p You will receive all notifications from projects in which you participate .form-actions - = f.submit 'Save changes', class: "btn btn-save" + = f.submit 'Save changes', class: "btn btn-create" .clearfix %hr @@ -60,7 +59,7 @@ %p You can also specify notification level per group or per project. %br - By default all projects and groups uses notification level set above. + By default, all projects and groups will use the notification level set above. %h4 Groups: %ul.bordered-list - @group_members.each do |users_group| @@ -69,7 +68,7 @@ .col-md-6 %p - To specify notification level per project of a group you belong to, + To specify the notification level per project of a group you belong to, %br you need to be a member of the project itself, not only its group. %h4 Projects: diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 2a7d317aa3e..4b04b113e89 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -1,25 +1,30 @@ -%h3.page-title Password +%h3.page-title Password Settings %p.light - Change your password or recover your current one. + - if @user.password_automatically_set? + Set your password. + - else + Change your password or recover your current one. %hr .update-password = form_for @user, url: profile_password_path, method: :put, html: { class: 'form-horizontal' } do |f| %div %p.slead - You must provide current password in order to change it. - %br - After a successful password update you will be redirected to login page where you should login with your new password + - unless @user.password_automatically_set? + You must provide current password in order to change it. + %br + After a successful password update, you will be redirected to the login page where you can log in with your new password. -if @user.errors.any? .alert.alert-danger %ul - @user.errors.full_messages.each do |msg| %li= msg - .form-group - = f.label :current_password, class: 'control-label' - .col-sm-10 - = f.password_field :current_password, required: true, class: 'form-control' - %div - = link_to "Forgot your password?", reset_profile_password_path, method: :put + - unless @user.password_automatically_set? + .form-group + = f.label :current_password, class: 'control-label' + .col-sm-10 + = f.password_field :current_password, required: true, class: 'form-control' + %div + = link_to "Forgot your password?", reset_profile_password_path, method: :put .form-group = f.label :password, 'New password', class: 'control-label' @@ -30,4 +35,4 @@ .col-sm-10 = f.password_field :password_confirmation, required: true, class: 'form-control' .form-actions - = f.submit 'Save password', class: "btn btn-save" + = f.submit 'Save password', class: "btn btn-create" diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index aef7348fd20..8bed6e0dbee 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -10,10 +10,11 @@ %ul - @user.errors.full_messages.each do |msg| %li= msg - - .form-group - = f.label :current_password, class: 'control-label' - .col-sm-10= f.password_field :current_password, required: true, class: 'form-control' + + - unless @user.password_automatically_set? + .form-group + = f.label :current_password, class: 'control-label' + .col-sm-10= f.password_field :current_password, required: true, class: 'form-control' .form-group = f.label :password, class: 'control-label' .col-sm-10= f.password_field :password, required: true, class: 'form-control' diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 640104fdad1..1a7bc353bf3 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -1,7 +1,7 @@ %h3.page-title - Profile settings + Profile Settings %p.light - This information appears on your profile. + This information will appear on your profile. - if current_user.ldap_user? Some options are unavailable for LDAP accounts %hr @@ -56,7 +56,7 @@ .form-group = f.label :bio, class: "control-label" .col-sm-10 - = f.text_area :bio, rows: 6, class: "form-control", maxlength: 250 + = f.text_area :bio, rows: 4, class: "form-control", maxlength: 250 %span.help-block Tell us about yourself in fewer than 250 characters. .col-md-5 @@ -89,10 +89,12 @@ = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" - if @user.public_profile? - .bs-callout.bs-callout-info + .alert.alert-info %h4 Public profile %p Your profile is publicly visible because you joined public project(s) - .form-actions - = f.submit 'Save changes', class: "btn btn-save" + .row + .col-md-7 + .col-sm-2 + = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml new file mode 100644 index 00000000000..07d4d602769 --- /dev/null +++ b/app/views/projects/_bitbucket_import_modal.html.haml @@ -0,0 +1,13 @@ +%div#bitbucket_import_modal.modal.hide + .modal-dialog + .modal-content + .modal-header + %a.close{href: "#", "data-dismiss" => "modal"} × + %h3 Import projects from Bitbucket + .modal-body + To enable importing projects from Bitbucket, + - if current_user.admin? + you need to + - else + your GitLab administrator needs to + == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/bitbucket.md'}. diff --git a/app/views/projects/_dropdown.html.haml b/app/views/projects/_dropdown.html.haml index 6ff46970336..2d5120f283b 100644 --- a/app/views/projects/_dropdown.html.haml +++ b/app/views/projects/_dropdown.html.haml @@ -9,24 +9,24 @@ New issue - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project) %li - = link_to new_project_merge_request_path(@project), title: "New Merge Request" do + = link_to new_namespace_project_merge_request_path(@project.namespace, @project), title: "New Merge Request" do New merge request - if @project.snippets_enabled && can?(current_user, :write_snippet, @project) %li - = link_to new_project_snippet_path(@project), title: "New Snippet" do + = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do New snippet - if can?(current_user, :admin_team_member, @project) %li - = link_to new_project_team_member_path(@project), title: "New project member" do + = link_to new_namespace_project_team_member_path(@project.namespace, @project), title: "New project member" do New project member - if can? current_user, :push_code, @project %li.divider %li - = link_to new_project_branch_path(@project) do + = link_to new_namespace_project_branch_path(@project.namespace, @project) do %i.fa.fa-code-fork Git branch %li - = link_to new_project_tag_path(@project) do + = link_to new_namespace_project_tag_path(@project.namespace, @project) do %i.fa.fa-tag Git tag diff --git a/app/views/projects/_github_import_modal.html.haml b/app/views/projects/_github_import_modal.html.haml index 99325e66119..e88a0f7d689 100644 --- a/app/views/projects/_github_import_modal.html.haml +++ b/app/views/projects/_github_import_modal.html.haml @@ -3,7 +3,11 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3 GitHub OAuth import + %h3 Import projects from GitHub .modal-body - You need to setup integration with GitHub first. - = link_to 'How to setup integration with GitHub', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/github.md'
\ No newline at end of file + To enable importing projects from GitHub, + - if current_user.admin? + you need to + - else + your GitLab administrator needs to + == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/github.md'}.
\ No newline at end of file diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml index e7503f023b1..52212b6ae02 100644 --- a/app/views/projects/_gitlab_import_modal.html.haml +++ b/app/views/projects/_gitlab_import_modal.html.haml @@ -3,7 +3,11 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3 GitLab OAuth import + %h3 Import projects from GitLab.com .modal-body - You need to setup integration with GitLab first. - = link_to 'How to setup integration with GitLab', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/gitlab.md'
\ No newline at end of file + To enable importing projects from GitLab.com, + - if current_user.admin? + you need to + - else + your GitLab administrator needs to + == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/gitlab.md'}.
\ No newline at end of file diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 5697f9ea1af..c0e13e67be3 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,28 +1,28 @@ - empty_repo = @project.empty_repo? .project-home-panel{:class => ("empty-project" if empty_repo)} .project-identicon-holder - = project_icon(@project.to_param, alt: '', class: 'avatar project-avatar') + = project_icon(@project, alt: '', class: 'avatar project-avatar') .project-home-row .project-home-desc - if @project.description.present? = escaped_autolink(@project.description) - if can?(current_user, :admin_project, @project) – - = link_to 'Edit', edit_project_path + = link_to 'Edit', edit_namespace_project_path - elsif !@project.empty_repo? && @repository.readme - readme = @repository.readme – - = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do + = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do = readme.name .star-fork-buttons - unless @project.empty_repo? .fork-buttons - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - = link_to project_path(current_user.fork_of(@project)), title: 'Go to my fork' do + = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to my fork' do = link_to_toggle_fork - else - = link_to new_project_fork_path(@project), title: "Fork project" do + = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project" do = link_to_toggle_fork .star-buttons diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml index 9e2e214b3e8..a7cd129b631 100644 --- a/app/views/projects/_issuable_form.html.haml +++ b/app/views/projects/_issuable_form.html.haml @@ -15,7 +15,7 @@ = f.label :description, 'Description', class: 'control-label' .col-sm-10 - = render layout: 'projects/md_preview' do + = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' .col-sm-12.hint @@ -23,7 +23,7 @@ Parsed with #{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}. .pull-right - Attach images (JPG, PNG, GIF) by dragging & dropping + Attach files by dragging & dropping or #{link_to 'selecting them', '#', class: 'markdown-selector' }. .clearfix @@ -50,10 +50,11 @@ = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, { class: 'select2' }) - else + .prepend-top-10 %span.light No open milestones available. - if can? current_user, :admin_milestone, issuable.project - = link_to 'Create new milestone', new_project_milestone_path(issuable.project), target: :blank + = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank .form-group = f.label :label_ids, class: 'control-label' do %i.fa.fa-tag @@ -63,10 +64,11 @@ = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, { selected: issuable.label_ids }, multiple: true, class: 'select2' - else + .prepend-top-10 %span.light No labels yet. - if can? current_user, :admin_label, issuable.project - = link_to 'Create new label', new_project_label_path(issuable.project), target: :blank + = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank .form-actions - if !issuable.project.empty_repo? && contribution_guide_url(issuable.project) && !issuable.persisted? @@ -82,4 +84,4 @@ - cancel_project = issuable.source_project - else - cancel_project = issuable.project - = link_to 'Cancel', [cancel_project, issuable], class: 'btn btn-cancel' + = link_to 'Cancel', [cancel_project.namespace.becomes(Namespace), cancel_project, issuable], class: 'btn btn-cancel' diff --git a/app/views/projects/_issues_nav.html.haml b/app/views/projects/_issues_nav.html.haml deleted file mode 100644 index f4e3d9a1093..00000000000 --- a/app/views/projects/_issues_nav.html.haml +++ /dev/null @@ -1,51 +0,0 @@ -%ul.nav.nav-tabs - - if project_nav_tab? :issues - = nav_link(controller: :issues) do - = link_to project_issues_path(@project), class: "tab" do - %i.fa.fa-exclamation-circle - Issues - - if project_nav_tab? :merge_requests - = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project), class: "tab" do - %i.fa.fa-tasks - Merge Requests - = nav_link(controller: :milestones) do - = link_to project_milestones_path(@project), class: "tab" do - %i.fa.fa-clock-o - Milestones - = nav_link(controller: :labels) do - = link_to project_labels_path(@project), class: "tab" do - %i.fa.fa-tags - Labels - - - - if current_controller?(:issues) - - if current_user - %li.hidden-xs - = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do - %i.fa.fa-rss - - %li.pull-right - .pull-right - .pull-left - = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do - .append-right-10.hidden-xs.hidden-sm - = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } - = hidden_field_tag :state, params['state'] - = hidden_field_tag :scope, params['scope'] - = hidden_field_tag :assignee_id, params['assignee_id'] - = hidden_field_tag :milestone_id, params['milestone_id'] - = hidden_field_tag :label_id, params['label_id'] - - - if can? current_user, :write_issue, @project - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do - %i.fa.fa-plus - New Issue - - - if current_controller?(:merge_requests) - %li.pull-right - .pull-right - - if can? current_user, :write_merge_request, @project - = link_to new_project_merge_request_path(@project), class: "btn btn-new pull-left", title: "New Merge Request" do - %i.fa.fa-plus - New Merge Request diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index cb75149434f..f356a25dbfa 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -4,10 +4,10 @@ Write %li = link_to '#md-preview-holder', class: 'js-md-preview-button', - data: { url: markdown_preview_project_path(@project) } do + data: { url: markdown_preview_namespace_project_path(@project.namespace, @project) } do Preview %div .md-write-holder = yield .md-preview-holder.hide - .js-md-preview + .js-md-preview{class: (preview_class if defined?(preview_class))} diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml index 646e48a1e1d..7fc3d44034f 100644 --- a/app/views/projects/_settings_nav.html.haml +++ b/app/views/projects/_settings_nav.html.haml @@ -5,27 +5,27 @@ %span Project = nav_link(controller: [:team_members, :teams]) do - = link_to project_team_index_path(@project), title: 'Members', class: "team-tab tab" do + = link_to namespace_project_team_index_path(@project.namespace, @project), title: 'Members', class: "team-tab tab" do %i.fa.fa-users %span Members = nav_link(controller: :deploy_keys) do - = link_to project_deploy_keys_path(@project), title: 'Deploy Keys' do + = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do %i.fa.fa-key %span Deploy Keys = nav_link(controller: :hooks) do - = link_to project_hooks_path(@project), title: 'Web Hooks' do + = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks' do %i.fa.fa-link %span Web Hooks = nav_link(controller: :services) do - = link_to project_services_path(@project), title: 'Services' do + = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do %i.fa.fa-cogs %span Services = nav_link(controller: :protected_branches) do - = link_to project_protected_branches_path(@project), title: 'Protected Branches' do + = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do %i.fa.fa-lock %span Protected branches diff --git a/app/views/projects/_visibility_level.html.haml b/app/views/projects/_visibility_level.html.haml index 5f34e66b3ed..42c8e685224 100644 --- a/app/views/projects/_visibility_level.html.haml +++ b/app/views/projects/_visibility_level.html.haml @@ -7,8 +7,8 @@ - Gitlab::VisibilityLevel.values.each do |level| .radio - restricted = restricted_visibility_levels.include?(level) - = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted = label :project_visibility_level, level do + = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted = visibility_level_icon(level) .option-title = visibility_level_label(level) diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 51a2f20d1e2..5a33d18e631 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -15,11 +15,11 @@ %tr %td.blame-commit %span.commit - = link_to commit.short_id, project_commit_path(@project, commit), class: "commit_short_id" + = link_to commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit), class: "commit_short_id" = commit_author_link(commit, avatar: true, size: 16) - = link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title" + = link_to_gfm truncate(commit.title, length: 20), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "row_title" %td.lines.blame-numbers %pre - (since...(since + lines.count)).each do |i| diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index f428ae41ef4..b5b29540bb6 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -1,19 +1,19 @@ .btn-group.tree-btn-group = edit_blob_link(@project, @ref, @path) - = link_to 'Raw', project_raw_path(@project, @id), + = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), class: 'btn btn-small', target: '_blank' -# only show normal/blame view links for text files - if @blob.text? - - if current_page? project_blame_path(@project, @id) - = link_to 'Normal View', project_blob_path(@project, @id), + - if current_page? namespace_project_blame_path(@project.namespace, @project, @id) + = link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id), class: 'btn btn-small' - else - = link_to 'Blame', project_blame_path(@project, @id), + = link_to 'Blame', namespace_project_blame_path(@project.namespace, @project, @id), class: 'btn btn-small' unless @blob.empty? - = link_to 'History', project_commits_path(@project, @id), + = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn btn-small' - if @ref != @commit.sha - = link_to 'Permalink', project_blob_path(@project, + = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.sha, @path)), class: 'btn btn-small' - if allowed_tree_edit? diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index 68f3b08b8c8..9ff61f3887f 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -1,21 +1,21 @@ %ul.breadcrumb.repo-breadcrumb %li %i.fa.fa-angle-right - = link_to project_tree_path(@project, @ref) do + = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do = @project.path - tree_breadcrumbs(@tree, 6) do |title, path| %li - if path - if path.end_with?(@path) - = link_to project_blob_path(@project, path) do + = link_to namespace_project_blob_path(@project.namespace, @project, path) do %strong = truncate(title, length: 40) - else - = link_to truncate(title, length: 40), project_tree_path(@project, path) + = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) - else = link_to title, '#' -%ul.blob-commit-info.bs-callout.bs-callout-info.hidden-xs +%ul.blob-commit-info.well.hidden-xs - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path) = render blob_commit, project: @project diff --git a/app/views/projects/blob/_download.html.haml b/app/views/projects/blob/_download.html.haml index c24eeea4931..f2c5e95ecf4 100644 --- a/app/views/projects/blob/_download.html.haml +++ b/app/views/projects/blob/_download.html.haml @@ -1,6 +1,6 @@ .file-content.blob_file.blob-no-preview .center - = link_to project_raw_path(@project, @id) do + = link_to namespace_project_raw_path(@project.namespace, @project, @id) do %h1.light %i.fa.fa-download %h4 diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index c5568315cb1..09559a4967b 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -9,7 +9,7 @@ %strong= @ref .modal-body - = form_tag project_blob_path(@project, @id), method: :delete, class: 'form-horizontal' do + = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal' do = render 'shared/commit_message_container', params: params, placeholder: 'Removed this file because...' .form-group diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index b150b639888..1f61a0b940c 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -6,14 +6,21 @@ Edit file %li - = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do + = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do %i.fa.fa-eye = editing_preview_title(@blob.name) - = form_tag(project_update_blob_path(@project, @id), method: :put, class: "form-horizontal") do + = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: "form-horizontal") do = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data = render 'shared/commit_message_container', params: params, placeholder: "Update #{@blob.name}" + + .form-group.branch + = label_tag 'branch', class: 'control-label' do + Branch + .col-sm-10 + = text_field_tag 'new_branch', @ref, class: "form-control" + = hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index df6aedbe17d..d78a01f6422 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,12 +1,19 @@ %h3.page-title New file .file-editor - = form_tag(project_create_blob_path(@project, @id), method: :post, class: 'form-horizontal form-new-file') do + = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do = render 'projects/blob/editor', ref: @ref = render 'shared/commit_message_container', params: params, placeholder: 'Add new file' + + .form-group.branch + = label_tag 'branch', class: 'control-label' do + Branch + .col-sm-10 + = text_field_tag 'new_branch', @ref, class: "form-control" + = hidden_field_tag 'content', '', id: 'file-content' = render 'projects/commit_button', ref: @ref, - cancel_path: project_tree_path(@project, @id) + cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) :javascript blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 8e58f3c247a..8de629b03e9 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -1,7 +1,7 @@ - commit = @repository.commit(branch.target) %li(class="js-branch-#{branch.name}") %h4 - = link_to project_tree_path(@project, branch.name) do + = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do %strong.str-truncated= branch.name - if branch.name == @repository.root_ref %span.label.label-info default @@ -13,12 +13,12 @@ - if can?(current_user, :download_code, @project) = render 'projects/repositories/download_archive', ref: branch.name, btn_class: 'btn-grouped btn-group-small' - if branch.name != @repository.root_ref - = link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name), class: 'btn btn-grouped btn-small', method: :post, title: "Compare" do + = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-grouped btn-small', method: :post, title: "Compare" do %i.fa.fa-files-o Compare - if can_remove_branch?(@project, branch.name) - = link_to project_branch_path(@project, branch.name), class: 'btn btn-grouped btn-small btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do + = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-small btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do %i.fa.fa-trash-o - if commit diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index d2aefd815a1..a313ffcf272 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -3,12 +3,12 @@ Branches .pull-right - if can? current_user, :push_code, @project - = link_to new_project_branch_path(@project), class: 'btn btn-create' do + = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do %i.fa.fa-add-sign New branch .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light sort: - if @sort.present? = @sort.humanize @@ -17,11 +17,11 @@ %b.caret %ul.dropdown-menu %li - = link_to project_branches_path(sort: nil) do + = link_to namespace_project_branches_path(sort: nil) do Name - = link_to project_branches_path(sort: 'recently_updated') do + = link_to namespace_project_branches_path(sort: 'recently_updated') do = sort_title_recently_updated - = link_to project_branches_path(sort: 'last_updated') do + = link_to namespace_project_branches_path(sort: 'last_updated') do = sort_title_oldest_updated %hr - unless @branches.empty? diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 2719bcc33bc..e5fcb98c68c 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -5,7 +5,7 @@ %h3.page-title %i.fa.fa-code-fork New branch -= form_tag project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal" do += form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal" do .form-group = label_tag :branch_name, 'Name for new branch', class: 'control-label' .col-sm-10 @@ -16,7 +16,7 @@ = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 - = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' + = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript disableButtonIfAnyEmptyField($("#new-branch-form"), ".form-control", ".btn-create"); diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index dd28a35d41d..7409f702c5d 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -10,15 +10,15 @@ Download as %span.caret %ul.dropdown-menu - %li= link_to "Email Patches", project_commit_path(@project, @commit, format: :patch) - %li= link_to "Plain Diff", project_commit_path(@project, @commit, format: :diff) - = link_to project_tree_path(@project, @commit), class: "btn btn-primary btn-grouped" do + %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch) + %li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff) + = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-primary btn-grouped" do %span Browse Code » %div %p %span.light Commit - = link_to @commit.id, project_commit_path(@project, @commit) + = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit) .commit-info-row %span.light Authored by %strong @@ -35,7 +35,7 @@ .commit-info-row %span.cgray= pluralize(@commit.parents.count, "parent") - @commit.parents.each do |parent| - = link_to parent.short_id, project_commit_path(@project, parent) + = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent) .commit-info-row.branches %i.fa.fa-spinner.fa-spin @@ -49,4 +49,4 @@ :coffeescript $ -> - $(".commit-info-row.branches").load("#{branches_project_commit_path(@project, @commit.id)}")
\ No newline at end of file + $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}") diff --git a/app/views/projects/commit/branches.html.haml b/app/views/projects/commit/branches.html.haml index b01e806210c..82aac1fbd15 100644 --- a/app/views/projects/commit/branches.html.haml +++ b/app/views/projects/commit/branches.html.haml @@ -1,7 +1,7 @@ - if @branches.any? %span - branch = commit_default_branch(@project, @branches) - = link_to(project_tree_path(@project, branch)) do + = link_to(namespace_project_tree_path(@project.namespace, @project, branch)) do %span.label.label-gray %i.fa.fa-code-fork = branch @@ -13,4 +13,4 @@ - if @branches.any? = commit_branches_links(@project, @branches) - if @tags.any? - = commit_tags_links(@project, @tags)
\ No newline at end of file + = commit_tags_links(@project, @tags) diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 1eb17f760dc..4c853f577e9 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -1,13 +1,12 @@ %li.commit.js-toggle-container .commit-row-title - = link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id" - - %span.str-truncated - = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" + %strong.str-truncated + = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" - if commit.description? %a.text-expander.js-toggle-button ... - = link_to_browse_code(project, commit) + .pull-right + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" .notes_count - if @note_counts @@ -17,8 +16,9 @@ - note_count = notes.count - if note_count > 0 - %span.label.label-gray - %i.fa.fa-comment= note_count + %span.light + %i.fa.fa-comments + = note_count - if commit.description? .commit-row-description.js-toggle-content @@ -26,6 +26,8 @@ = preserve(gfm(escape_once(commit.description))) .commit-row-info - = commit_author_link(commit, avatar: true, size: 16) + = commit_author_link(commit, avatar: true, size: 24) + authored .committed_ago #{time_ago_with_tooltip(commit.committed_date)} + = link_to_browse_code(project, commit) diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index 2d0ca671fa0..0cd9ce1f371 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -3,12 +3,13 @@ - @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits| .row.commits-row - .col-md-2 - %h4 + .col-md-2.hidden-xs.hidden-sm + %h5.commits-row-date %i.fa.fa-calendar %span= day.stamp("28 Aug, 2010") - %p= pluralize(commits.count, 'commit') - .col-md-10 + .light + = pluralize(commits.count, 'commit') + .col-md-10.col-sm-12 %ul.bordered-list = render commits, project: project %hr.lists-separator diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 0c9d906481b..83e4d24cf5f 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -1,15 +1,15 @@ %ul.nav.nav-tabs = nav_link(controller: [:commit, :commits]) do - = link_to 'Commits', project_commits_path(@project, @repository.root_ref) + = link_to 'Commits', namespace_project_commits_path(@project.namespace, @project, @repository.root_ref) = nav_link(controller: :compare) do - = link_to 'Compare', project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref) + = link_to 'Compare', namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref) = nav_link(html_options: {class: branches_tab_class}) do - = link_to project_branches_path(@project) do + = link_to namespace_project_branches_path(@project.namespace, @project) do Branches %span.badge.js-totalbranch-count= @repository.branches.size = nav_link(controller: :tags) do - = link_to project_tags_path(@project) do + = link_to namespace_project_tags_path(@project.namespace, @project) do Tags %span.badge.js-totaltags-count= @repository.tags.length diff --git a/app/views/projects/commits/_inline_commit.html.haml b/app/views/projects/commits/_inline_commit.html.haml index 574599aa2d2..c03bc3f9df9 100644 --- a/app/views/projects/commits/_inline_commit.html.haml +++ b/app/views/projects/commits/_inline_commit.html.haml @@ -1,8 +1,8 @@ %li.commit.inline-commit .commit-row-title - = link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id" + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" %span.str-truncated - = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" + = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" .pull-right #{time_ago_with_tooltip(commit.committed_date)} diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 32c82edb248..9211de72b1b 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -1,15 +1,15 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "Recent commits to #{@project.name}:#{@ref}" - xml.link :href => project_commits_url(@project, @ref, format: :atom), :rel => "self", :type => "application/atom+xml" - xml.link :href => project_commits_url(@project, @ref), :rel => "alternate", :type => "text/html" - xml.id project_commits_url(@project, @ref) + xml.link :href => namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom), :rel => "self", :type => "application/atom+xml" + xml.link :href => namespace_project_commits_url(@project.namespace, @project, @ref), :rel => "alternate", :type => "text/html" + xml.id namespace_project_commits_url(@project.namespace, @project, @ref) xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any? @commits.each do |commit| xml.entry do - xml.id project_commit_url(@project, :id => commit.id) - xml.link :href => project_commit_url(@project, :id => commit.id) + xml.id namespace_project_commit_url(@project.namespace, @project, :id => commit.id) + xml.link :href => namespace_project_commit_url(@project.namespace, @project, :id => commit.id) xml.title truncate(commit.title, :length => 80) xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(commit.author_email) diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index b80639763c8..7ea855e1a4e 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -5,7 +5,7 @@ - if current_user && current_user.private_token .commits-feed-holder.hidden-xs.hidden-sm - = link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed", class: 'btn' do + = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed", class: 'btn' do %i.fa.fa-rss Commits feed diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index cb0a3747f7d..dfb1dded9ea 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -1,4 +1,4 @@ -= form_tag project_compare_index_path(@project), method: :post, class: 'form-inline' do += form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline' do .clearfix.append-bottom-20 - if params[:to] && params[:from] = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'} diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml index a0345dbd9c3..230e164f24c 100644 --- a/app/views/projects/deploy_keys/_deploy_key.html.haml +++ b/app/views/projects/deploy_keys/_deploy_key.html.haml @@ -1,19 +1,20 @@ %li .pull-right - if @available_keys.include?(deploy_key) - = link_to enable_project_deploy_key_path(@project, deploy_key), class: 'btn btn-small', method: :put do + = link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-small', method: :put do %i.fa.fa-plus Enable - else - if deploy_key.projects.count > 1 - = link_to disable_project_deploy_key_path(@project, deploy_key), class: 'btn btn-small', method: :put do + = link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-small', method: :put do %i.fa.fa-power-off Disable - else - = link_to 'Remove', project_deploy_key_path(@project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :delete, class: "btn btn-remove delete-key btn-small pull-right" + = link_to 'Remove', namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :delete, class: "btn btn-remove delete-key btn-small pull-right" - = link_to project_deploy_key_path(deploy_key.projects.include?(@project) ? @project : deploy_key.projects.first, deploy_key) do + - key_project = deploy_key.projects.include?(@project) ? @project : deploy_key.projects.first + = link_to namespace_project_deploy_key_path(key_project.namespace, key_project, deploy_key) do %i.fa.fa-key %strong= deploy_key.title diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 162ef05b367..91675b3738e 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -1,5 +1,5 @@ %div - = form_for [@project, @key], url: project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f| + = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f| -if @key.errors.any? .alert.alert-danger %ul @@ -19,5 +19,5 @@ .form-actions = f.submit 'Create', class: "btn-create btn" - = link_to "Cancel", project_deploy_keys_path(@project), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/deploy_keys/index.html.haml b/app/views/projects/deploy_keys/index.html.haml index 6f475e0b395..c02a18146eb 100644 --- a/app/views/projects/deploy_keys/index.html.haml +++ b/app/views/projects/deploy_keys/index.html.haml @@ -1,7 +1,7 @@ %h3.page-title Deploy keys allow read-only access to the repository - = link_to new_project_deploy_key_path(@project), class: "btn btn-new pull-right", title: "New Deploy Key" do + = link_to new_namespace_project_deploy_key_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Deploy Key" do %i.fa.fa-plus New Deploy Key @@ -20,7 +20,7 @@ = render @enabled_keys - if @enabled_keys.blank? .light-well - .nothing-here-block Create a #{link_to 'new deploy key', new_project_deploy_key_path(@project)} or add an existing one + .nothing-here-block Create a #{link_to 'new deploy key', new_namespace_project_deploy_key_path(@project.namespace, @project)} or add an existing one .col-md-6.available-keys %h5 %strong Deploy keys diff --git a/app/views/projects/deploy_keys/show.html.haml b/app/views/projects/deploy_keys/show.html.haml index c66e6bc69c3..405b5bcd0d3 100644 --- a/app/views/projects/deploy_keys/show.html.haml +++ b/app/views/projects/deploy_keys/show.html.haml @@ -5,9 +5,9 @@ created on = @key.created_at.stamp("Aug 21, 2011") .back-link - = link_to project_deploy_keys_path(@project) do + = link_to namespace_project_deploy_keys_path(@project.namespace, @project) do ← To keys list %hr %pre= @key.key .pull-right - = link_to 'Remove', project_deploy_key_path(@project, @key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn-remove btn delete-key" + = link_to 'Remove', namespace_project_deploy_key_path(@project.namespace, @project, @key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn-remove btn delete-key" diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 8d080f710d8..2569e91ccfa 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,6 +1,6 @@ - blob = project.repository.blob_for_diff(@commit, diff_file.diff) - return unless blob -- blob_diff_path = diff_project_blob_path(project, tree_join(@commit.id, diff_file.file_path)) +- blob_diff_path = namespace_project_blob_diff_path(project.namespace, project, tree_join(@commit.id, diff_file.file_path)) .diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }} .diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"} - if diff_file.deleted_file diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml index 900646dd0a4..058b71b21f5 100644 --- a/app/views/projects/diffs/_image.html.haml +++ b/app/views/projects/diffs/_image.html.haml @@ -10,7 +10,7 @@ %div.two-up.view %span.wrap .frame.deleted - %a{href: project_blob_path(@project, tree_join(@commit.parent_id, diff.old_path))} + %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))} %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} %p.image-info.hide %span.meta-filesize= "#{number_to_human_size old_file.size}" @@ -22,7 +22,7 @@ %span.meta-height %span.wrap .frame.added - %a{href: project_blob_path(@project, tree_join(@commit.id, diff.new_path))} + %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))} %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} %p.image-info.hide %span.meta-filesize= "#{number_to_human_size file.size}" diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml index 20e51d18da5..9b5eb84a86d 100644 --- a/app/views/projects/diffs/_stats.html.haml +++ b/app/views/projects/diffs/_stats.html.haml @@ -26,7 +26,7 @@ %a{href: "#diff-#{i}"} %i.fa.fa-minus = diff.old_path - \-> + → = diff.new_path - elsif diff.new_file %span.new-file diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml index 86ed6bbeaa2..af1f342afbd 100644 --- a/app/views/projects/diffs/_warning.html.haml +++ b/app/views/projects/diffs/_warning.html.haml @@ -1,4 +1,4 @@ -.bs-callout.bs-callout-warning +.alert.alert-warning %h4 Too many changes. .pull-right @@ -7,13 +7,13 @@ - if current_controller?(:commit) or current_controller?(:merge_requests) - if current_controller?(:commit) - = link_to "Plain diff", project_commit_path(@project, @commit, format: :diff), class: "btn btn-warning btn-small" - = link_to "Email patch", project_commit_path(@project, @commit, format: :patch), class: "btn btn-warning btn-small" + = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-warning btn-small" + = link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-warning btn-small" - elsif @merge_request && @merge_request.persisted? - = link_to "Plain diff", project_merge_request_path(@project, @merge_request, format: :diff), class: "btn btn-warning btn-small" - = link_to "Email patch", project_merge_request_path(@project, @merge_request, format: :patch), class: "btn btn-warning btn-small" + = link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-warning btn-small" + = link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-warning btn-small" %p To preserve performance only %strong #{allowed_diff_size} of #{diffs.size} - files displayed. + files are displayed. diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 737cda411bc..b4c36beda88 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -6,7 +6,7 @@ Project settings %hr .panel-body - = form_for @project, remote: true, html: { multipart: true, class: "edit_project form-horizontal" }, authenticity_token: true do |f| + = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit_project form-horizontal" }, authenticity_token: true do |f| %fieldset .form-group.project_name_holder @@ -78,7 +78,7 @@ .col-sm-2 .col-sm-10 - if @project.avatar? - = project_icon(@project.to_param, alt: '', class: 'avatar project-avatar s160') + = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160') %p.light - if @project.avatar_in_git Project avatar in repository: #{ @project.avatar_in_git } @@ -86,7 +86,7 @@ - if @project.avatar? You can change your project avatar here - else - You can upload an project avatar here + You can upload a project avatar here %a.choose-btn.btn.btn-small.js-choose-project-avatar-button %i.icon-paper-clip %span Choose File ... @@ -96,7 +96,7 @@ .light The maximum file size allowed is 200KB. - if @project.avatar? %hr - = link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" + = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" .form-actions = f.submit 'Save changes', class: "btn btn-save" @@ -116,7 +116,7 @@ The project can be committed to. %br %strong Once active this project shows up in the search and on the dashboard. - = link_to 'Unarchive', unarchive_project_path(@project), + = link_to 'Unarchive', unarchive_namespace_project_path(@project.namespace, @project), data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, method: :post, class: "btn btn-success" - else @@ -130,7 +130,7 @@ It is hidden from the dashboard and doesn't show up in searches. %br %strong Archived projects cannot be committed to! - = link_to 'Archive', archive_project_path(@project), + = link_to 'Archive', archive_namespace_project_path(@project.namespace, @project), data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, method: :post, class: "btn btn-warning" - else @@ -140,7 +140,7 @@ .panel-heading Rename repository .errors-holder .panel-body - = form_for(@project, html: { class: 'form-horizontal' }) do |f| + = form_for([@project.namespace.becomes(Namespace), @project], html: { class: 'form-horizontal' }) do |f| .form-group.project_name_holder = f.label :name, class: 'control-label' do Project name @@ -168,13 +168,13 @@ .panel-heading Transfer project .errors-holder .panel-body - = form_for(@project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| + = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| .form-group - = f.label :namespace_id, class: 'control-label' do + = label_tag :new_namespace_id, nil, class: 'control-label' do %span Namespace .col-sm-10 .form-group - = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'select2' } + = select_tag :new_namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace', class: 'select2' } %ul %li Be careful. Changing the project's namespace can have unintended side effects. %li You can only transfer the project to namespaces you manage. @@ -188,7 +188,7 @@ .panel.panel-default.panel.panel-danger .panel-heading Remove project .panel-body - = form_tag(project_path(@project), method: :delete, html: { class: 'form-horizontal'}) do + = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, html: { class: 'form-horizontal'}) do %p Removing the project will delete its repository and all related resources including issues, merge requests etc. %br diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index d7dee2208de..49806ceaa96 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,5 +1,6 @@ - if current_user && can?(current_user, :download_code, @project) = render 'shared/no_ssh' + = render 'shared/no_password' = render "home_panel" @@ -8,7 +9,7 @@ The repository for this project is empty %h4 You can - = link_to project_new_blob_path(@project, 'master'), class: 'btn btn-new btn-lg' do + = link_to namespace_project_new_blob_path(@project.namespace, @project, 'master'), class: 'btn btn-new btn-lg' do add a file or do a push via the command line. @@ -45,4 +46,4 @@ - if can? current_user, :remove_project, @project .prepend-top-20 - = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" + = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml index 76d3aa5bf00..8eb4f795971 100644 --- a/app/views/projects/forks/error.html.haml +++ b/app/views/projects/forks/error.html.haml @@ -15,6 +15,6 @@ = @forked_project.errors.full_messages.first %p - = link_to new_project_fork_path(@project), title: "Fork", class: "btn" do + = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork", class: "btn" do %i.fa.fa-code-fork Try to Fork again diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index 959d5f08d47..5a6c46f3208 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -18,7 +18,7 @@ = namespace.path - else .thumbnail.fork-thumbnail - = link_to project_fork_path(@project, namespace_id: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do + = link_to namespace_project_fork_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do = image_tag namespace_icon(namespace, 200) .caption %h4=namespace.human_name diff --git a/app/views/projects/go_import.html.haml b/app/views/projects/go_import.html.haml new file mode 100644 index 00000000000..87ac75a350f --- /dev/null +++ b/app/views/projects/go_import.html.haml @@ -0,0 +1,5 @@ +!!! 5 +%html + %head + - web_url = [Gitlab.config.gitlab.url, @namespace, @id].join('/') + %meta{name: "go-import", content: "#{web_url.split('://')[1]} git #{web_url}.git"} diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index 9f37a760e61..9383df13305 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -1,5 +1,5 @@ %ul.nav.nav-tabs = nav_link(action: :show) do - = link_to 'Contributors', project_graph_path + = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do - = link_to 'Commits', commits_project_graph_path + = link_to 'Commits', commits_namespace_project_graph_path diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index a189a487135..4a5d09b9503 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -1,7 +1,7 @@ = render 'head' %p.lead - Commits statistic for + Commit statistics for %strong #{@repository.root_ref} #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')} diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 9a003c87f68..e70cf5c3884 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -7,7 +7,7 @@ %hr.clearfix -= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-horizontal' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| -if @hook.errors.any? .alert.alert-danger - @hook.errors.full_messages.each do |msg| @@ -58,8 +58,8 @@ - @hooks.each do |hook| %li .pull-right - = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small btn-grouped" - = link_to 'Remove', project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-small btn-grouped" + = link_to 'Test Hook', test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-small btn-grouped" + = link_to 'Remove', namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-small btn-grouped" .clearfix %span.monospace= hook.url %p diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 6c3083e49f5..f1248ac2af5 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -6,13 +6,13 @@ %hr -= form_for @project, url: project_import_path(@project), method: :post, html: { class: 'form-horizontal' } do |f| += form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| .form-group.import-url-data = f.label :import_url, class: 'control-label' do %span Import existing git repo .col-sm-10 = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .bs-callout.bs-callout-info + .alert.alert-info This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. %br The import will time out after 4 minutes. For big repositories, use a clone/push combination. diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index e04e1985f1f..fc3e35640dc 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -1,37 +1,37 @@ - content_for :note_actions do - if can?(current_user, :modify_issue, @issue) - if @issue.closed? - = link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue' + = link_to 'Reopen Issue', issue_path(@issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue' - else - = link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue" + = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue" .row - .col-md-9 + %section.col-md-9 .participants - %cite.cgray - = pluralize(@issue.participants.count, 'participant') + %span= pluralize(@issue.participants.count, 'participant') - @issue.participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) .voting_notes#notes= render "projects/notes/notes_with_form" - .col-md-3 - %div - .clearfix - %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} - = cross_project_reference(@project, @issue) - %hr - .context - %cite.cgray + %aside.col-md-3 + .issuable-affix + .clearfix + %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} + = cross_project_reference(@project, @issue) + %hr + .context = render partial: 'issue_context', locals: { issue: @issue } - %hr - .clearfix - .votes-holder - %h6 Votes - #votes= render 'votes/votes_block', votable: @issue - - - if @issue.labels.any? %hr - %h6 Labels - .issue-show-labels - - @issue.labels.each do |label| - = link_to project_issues_path(@project, label_name: label.name) do - %p= render_colored_label(label) + .clearfix + .votes-holder + %h6 Votes + #votes= render 'votes/votes_block', votable: @issue + + - if @issue.labels.any? + %hr + %h6 Labels + .issue-show-labels + - @issue.labels.each do |label| + = link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do + = render_colored_label(label) + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 2a7b44955cd..7d7217eb2a8 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,8 +1,8 @@ %div.issue-form-holder - %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" + %h3.page-title= @issue.new_record? ? "Create Issue" : "Edit Issue ##{@issue.iid}" %hr - = form_for [@project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f| + = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f| = render 'projects/issuable_form', f: f, issuable: @issue :javascript @@ -11,4 +11,4 @@ e.preventDefault(); }); - window.project_image_path_upload = "#{upload_image_project_path @project}"; + window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index dc6510be858..01e2133e283 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -1,14 +1,20 @@ -%li{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) } +%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue) } - if controller.controller_name == 'issues' .issue-check = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) .issue-title %span.str-truncated - = link_to_gfm issue.title, project_issue_path(issue.project, issue), class: "row_title" - - if issue.closed? - %small.pull-right - CLOSED + = link_to_gfm issue.title, issue_path(issue), class: "row_title" + .pull-right.light + - if issue.closed? + %span + CLOSED + - if issue.notes.any? + + %span + %i.fa.fa-comments + = issue.notes.count .issue-info %span.light= "##{issue.iid}" @@ -16,10 +22,6 @@ assigned to #{link_to_member(@project, issue.assignee)} - if issue.votes_count > 0 = render 'votes/votes_inline', votable: issue - - if issue.notes.any? - %span - %i.fa.fa-comments - = issue.notes.count - if issue.milestone %span %i.fa.fa-clock-o @@ -33,16 +35,16 @@ .issue-labels - issue.labels.each do |label| - = link_to project_issues_path(issue.project, label_name: label.name) do + = link_to namespace_project_issues_path(issue.project.namespace, issue.project, label_name: label.name) do = render_colored_label(label) .issue-actions - if can? current_user, :modify_issue, issue - if issue.closed? - = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small btn-grouped reopen_issue btn-reopen", remote: true + = link_to 'Reopen', issue_path(issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small btn-grouped reopen_issue btn-reopen", remote: true - else - = link_to 'Close', project_issue_path(issue.project, issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small btn-grouped close_issue btn-close", remote: true - = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link btn-grouped" do + = link_to 'Close', issue_path(issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small btn-grouped close_issue btn-close", remote: true + = link_to edit_namespace_project_issue_path(issue.project.namespace, issue.project, issue), class: "btn btn-small edit-issue-link btn-grouped" do %i.fa.fa-pencil-square-o Edit diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index 3daa18ba346..4c7654354f4 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -1,19 +1,25 @@ -= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| += form_for [@project.namespace.becomes(Namespace), @project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| %div.prepend-top-20 - %p - Assignee: + .issuable-context-title + %label + Assignee: - if issue.assignee - = link_to_member(@project, @issue.assignee) + %strong= link_to_member(@project, @issue.assignee, size: 24) - else none - if can?(current_user, :modify_issue, @issue) = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id) - %div.prepend-top-20 - %p - Milestone: + %div.prepend-top-20.clearfix + .issuable-context-title + %label + Milestone: - if issue.milestone - #{link_to @issue.milestone.title, project_milestone_path(@project, @issue.milestone)} + %span.back-to-milestone + = link_to namespace_project_milestone_path(@project.namespace, @project, @issue.milestone) do + %strong + %i.fa.fa-clock-o + = @issue.milestone.title - else none - if can?(current_user, :modify_issue, @issue) diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index 816851a8abe..5d243adb5fe 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -1,18 +1,3 @@ -.append-bottom-10 - .check-all-holder - = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left", disabled: !can?(current_user, :modify_issue, @project) - = render 'shared/issuable_filter' - - .clearfix - .issues_bulk_update.hide - = form_tag bulk_update_project_issues_path(@project), method: :post do - = select_tag('update[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status") - = project_users_select_tag('update[assignee_id]', placeholder: 'Assignee') - = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") - = hidden_field_tag 'update[issues_ids]', [] - = hidden_field_tag :status, params[:status] - = button_tag "Update issues", class: "btn update_selected_issues btn-save" - .panel.panel-default %ul.well-list.issues-list = render @issues diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index 61e651da932..126f2c07faa 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -1,9 +1,9 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@project.name} issues" - xml.link :href => project_issues_url(@project, :atom), :rel => "self", :type => "application/atom+xml" - xml.link :href => project_issues_url(@project), :rel => "alternate", :type => "text/html" - xml.id project_issues_url(@project) + xml.link :href => namespace_project_issues_url(@project.namespace, @project, :atom), :rel => "self", :type => "application/atom+xml" + xml.link :href => namespace_project_issues_url(@project.namespace, @project), :rel => "alternate", :type => "text/html" + xml.id namespace_project_issues_url(@project.namespace, @project) xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? @issues.each do |issue| diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 0d00d6bfded..2cb94d10b6f 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -1,4 +1,29 @@ -= render "projects/issues_nav" +.append-bottom-10 + .pull-right + .pull-left + - if current_user + .hidden-xs.pull-left + = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do + %i.fa.fa-rss + + = render 'shared/issuable_search_form', path: namespace_project_issues_path(@project.namespace, @project) + + - if can? current_user, :write_issue, @project + = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do + %i.fa.fa-plus + New Issue + + = render 'shared/issuable_filter' + + .clearfix + .issues_bulk_update.hide + = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do + = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status") + = project_users_select_tag('update[assignee_id]', placeholder: 'Assignee') + = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") + = hidden_field_tag 'update[issues_ids]', [] + = hidden_field_tag :state_event, params[:state_event] + = button_tag "Update issues", class: "btn update_selected_issues btn-save" .issues-holder = render "issues" diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 75411c6d86f..bd28d8a1db2 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,37 +1,40 @@ -%h4.page-title - .issue-box{ class: issue_box_class(@issue) } - - if @issue.closed? - Closed - - else - Open - Issue ##{@issue.iid} - %small.creator - · created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)} +.issue + .issue-details + %h4.page-title + .issue-box{ class: issue_box_class(@issue) } + - if @issue.closed? + Closed + - else + Open + Issue ##{@issue.iid} + %small.creator + · created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)} - .pull-right - - if can?(current_user, :write_issue, @project) - = link_to new_project_issue_path(@project), class: "btn btn-grouped new-issue-link", title: "New Issue", id: "new_issue_link" do - %i.fa.fa-plus - New Issue - - if can?(current_user, :modify_issue, @issue) - - if @issue.closed? - = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen" - - else - = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue" + .pull-right + - if can?(current_user, :write_issue, @project) + = link_to new_namespace_project_issue_path(@project.namespace, @project), class: "btn btn-grouped new-issue-link", title: "New Issue", id: "new_issue_link" do + %i.fa.fa-plus + New Issue + - if can?(current_user, :modify_issue, @issue) + - if @issue.closed? + = link_to 'Reopen', issue_path(@issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen" + - else + = link_to 'Close', issue_path(@issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue" - = link_to edit_project_issue_path(@project, @issue), class: "btn btn-grouped issuable-edit" do - %i.fa.fa-pencil-square-o - Edit + = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: "btn btn-grouped issuable-edit" do + %i.fa.fa-pencil-square-o + Edit -%hr -%h3.issue-title - = gfm escape_once(@issue.title) -%div - - if @issue.description.present? - .description - .wiki - = preserve do - = markdown(@issue.description, parse_tasks: true) + %hr + %h2.issue-title + = gfm escape_once(@issue.title) + %div + - if @issue.description.present? + .description + .wiki + = preserve do + = markdown(@issue.description, parse_tasks: true) -%hr -= render "projects/issues/discussion" + %hr + .issue-discussion + = render "projects/issues/discussion" diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml index 7a5e0517556..82c0e653759 100644 --- a/app/views/projects/issues/update.js.haml +++ b/app/views/projects/issues/update.js.haml @@ -6,7 +6,7 @@ $('.context').html("#{escape_javascript(render partial: 'issue_context', locals: { issue: @issue })}"); $('.context').effect('highlight'); - if @issue.milestone - $('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, project_milestone_path(@issue.project, @issue.milestone))}</span>") + $('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, namespace_project_milestone_path(@issue.project.namespace, @issue.project, @issue.milestone))}</span>") - else $('.milestone-nav-link').html('') diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 72a01e1c271..2305fce112e 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -1,8 +1,8 @@ -= form_for [@project, @label], html: { class: 'form-horizontal label-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form' } do |f| -if @label.errors.any? .row .col-sm-10.col-sm-offset-2 - .bs-callout.bs-callout-danger + .alert.alert-danger - @label.errors.full_messages.each do |msg| %span= msg %br @@ -16,7 +16,7 @@ .col-sm-10 .input-group .input-group-addon.label-color-preview - = f.text_field :color, placeholder: "#AA33EE", class: "form-control" + = f.color_field :color, placeholder: "#AA33EE", class: "form-control" .help-block 6 character hex values starting with a # sign. %br @@ -29,5 +29,5 @@ .form-actions = f.submit 'Save', class: 'btn btn-save js-save-button' - = link_to "Cancel", project_labels_path(@project), class: 'btn btn-cancel' + = link_to "Cancel", namespace_project_labels_path(@project.namespace, @project), class: 'btn btn-cancel' diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index 03a8f0921b7..82829452862 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -2,9 +2,9 @@ = render_colored_label(label) .pull-right %strong.append-right-20 - = link_to project_issues_path(@project, label_name: label.name) do + = link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do = pluralize label.open_issues_count, 'open issue' - if can? current_user, :admin_label, @project - = link_to 'Edit', edit_project_label_path(@project, label), class: 'btn' - = link_to 'Remove', project_label_path(@project, label), class: 'btn btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} + = link_to 'Edit', edit_namespace_project_label_path(@project.namespace, @project, label), class: 'btn' + = link_to 'Remove', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index 52435c5d892..e003d1dfe7f 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -2,7 +2,7 @@ Edit label %span.light #{@label.name} .back-link - = link_to project_labels_path(@project) do + = link_to namespace_project_labels_path(@project.namespace, @project) do ← To labels list %hr = render 'form' diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index c7c17c7797e..0700e72d39c 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,7 +1,5 @@ -= render "projects/issues_nav" - - if can? current_user, :admin_label, @project - = link_to new_project_label_path(@project), class: "pull-right btn btn-new" do + = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do New label %h3.page-title Labels @@ -14,4 +12,4 @@ = paginate @labels, theme: 'gitlab' - else .light-well - .nothing-here-block Create first label or #{link_to 'generate', generate_project_labels_path(@project), method: :post} default set of labels + .nothing-here-block Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 850da0b192b..0683ed5d4fb 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,6 +1,6 @@ %h3 New label .back-link - = link_to project_labels_path(@project) do + = link_to namespace_project_labels_path(@project.namespace, @project) do ← To labels list %hr = render 'form' diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index f1f66569a9f..79a093dc775 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -1,31 +1,33 @@ - content_for :note_actions do - if can?(current_user, :modify_merge_request, @merge_request) - if @merge_request.open? - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request" + = link_to 'Close', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request" - if @merge_request.closed? - = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" .row - .col-md-9 + %section.col-md-9 = render "projects/merge_requests/show/participants" = render "projects/notes/notes_with_form" - .col-md-3 - .clearfix - %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} - = cross_project_reference(@project, @merge_request) - %hr - .context - %cite.cgray + %aside.col-md-3 + .issuable-affix + .clearfix + %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} + = cross_project_reference(@project, @merge_request) + %hr + .context = render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request } - %hr - .votes-holder - %h6 Votes - #votes= render 'votes/votes_block', votable: @merge_request - - - if @merge_request.labels.any? %hr - %h6 Labels - .merge-request-show-labels - - @merge_request.labels.each do |label| - = link_to project_merge_requests_path(@project, label_name: label.name) do - %p= render_colored_label(label) + .votes-holder + %h6 Votes + #votes= render 'votes/votes_block', votable: @merge_request + + - if @merge_request.labels.any? + %hr + %h6 Labels + .merge-request-show-labels + - @merge_request.labels.each do |label| + = link_to namespace_project_merge_requests_path(@project.namespace, @project, label_name: label.name) do + = render_colored_label(label) + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index d52e64666a0..1c7160bce5f 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f| .merge-request-form-info = render 'projects/issuable_form', f: f, issuable: @merge_request @@ -9,4 +9,4 @@ e.preventDefault(); }); - window.project_image_path_upload = "#{upload_image_project_path @project}"; + window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/merge_requests/_head.html.haml b/app/views/projects/merge_requests/_head.html.haml index 35a86e6511c..19e4dab874b 100644 --- a/app/views/projects/merge_requests/_head.html.haml +++ b/app/views/projects/merge_requests/_head.html.haml @@ -1,5 +1,5 @@ .top-tabs - = link_to project_merge_requests_path(@project), class: "tab #{'active' if current_page?(project_merge_requests_path(@project)) }" do + = link_to namespace_project_merge_requests_path(@project.namespace, @project), class: "tab #{'active' if current_page?(namespace_project_merge_requests_path(@project.namespace, @project)) }" do %span Merge Requests diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index dedb060a231..1eba1a96b7b 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,28 +1,34 @@ %li{ class: mr_css_classes(merge_request) } .merge-request-title - = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title" - - if merge_request.merged? - %small.pull-right - %i.fa.fa-check - MERGED - - else - %span.pull-right.hidden-xs - - if merge_request.for_fork? - %span.light - #{merge_request.source_project_namespace}: - = truncate merge_request.source_branch, length: 25 - %i.fa.fa-angle-right.light - = merge_request.target_branch + %span.str-truncated + = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" + .pull-right.light + - if merge_request.merged? + %span + %i.fa.fa-check + MERGED + - elsif merge_request.closed? + %span + %i.fa.fa-close + CLOSED + - else + %span.hidden-xs.hidden-sm + %span.label-branch< + %i.fa.fa-code-fork + %span= merge_request.target_branch + - if merge_request.notes.any? + + %span + %i.fa.fa-comments + = merge_request.mr_and_commit_notes.count .merge-request-info %span.light= "##{merge_request.iid}" - - if merge_request.author - authored by #{link_to_member(merge_request.source_project, merge_request.author)} + - if merge_request.assignee + assigned to #{link_to_member(merge_request.source_project, merge_request.assignee)} + - else + Unassigned - if merge_request.votes_count > 0 = render 'votes/votes_inline', votable: merge_request - - if merge_request.notes.any? - %span - %i.fa.fa-comments - = merge_request.mr_and_commit_notes.count - if merge_request.milestone_id? %span %i.fa.fa-clock-o @@ -31,10 +37,11 @@ %span.task-status = merge_request.task_status + .pull-right.hidden-xs %small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')} .merge-request-labels - merge_request.labels.each do |label| - = link_to project_merge_requests_path(merge_request.project, label_name: label.name) do + = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, label_name: label.name) do = render_colored_label(label) diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml new file mode 100644 index 00000000000..b8a0ca9a42f --- /dev/null +++ b/app/views/projects/merge_requests/_merge_requests.html.haml @@ -0,0 +1,13 @@ +.panel.panel-default + %ul.well-list.mr-list + = render @merge_requests + - if @merge_requests.blank? + %li + .nothing-here-block No merge requests to show + +- if @merge_requests.present? + .pull-right + %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter + + = paginate @merge_requests, theme: "gitlab" + diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index 99726172154..17e76059fdb 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -1,7 +1,7 @@ %h3.page-title Compare branches for new Merge Request %hr -= form_for [@project, @merge_request], url: new_project_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline" } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: new_namespace_project_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline" } do |f| .hide.alert.alert-danger.mr-compare-errors .merge-request-branches.row .col-md-6 @@ -60,19 +60,19 @@ , target_branch = $("#merge_request_target_branch") , target_project = $("#merge_request_target_project_id"); - $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() }); - $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() }); + $.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: source_branch.val() }); + $.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() }); target_project.on("change", function() { - $.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() }); + $.get("#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: $(this).val() }); }); source_branch.on("change", function() { - $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() }); + $.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: $(this).val() }); $(".mr-compare-errors").fadeOut(); $(".mr-compare-btn").enable(); }); target_branch.on("change", function() { - $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() }); + $.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: $(this).val() }); $(".mr-compare-errors").fadeOut(); $(".mr-compare-btn").enable(); }); diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index ac374532ffd..bf80afe8785 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -7,9 +7,9 @@ %strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch} %span.pull-right - = link_to 'Change branches', new_project_merge_request_path(@project) + = link_to 'Change branches', new_namespace_project_merge_request_path(@project.namespace, @project) -= form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal gfm-form" } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: "merge-request-form form-horizontal gfm-form" } do |f| .merge-request-form-info .form-group = f.label :title, class: 'control-label' do @@ -19,7 +19,7 @@ .form-group.issuable-description = f.label :description, 'Description', class: 'control-label' .col-sm-10 - = render layout: 'projects/md_preview' do + = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' .col-sm-12-hint @@ -27,7 +27,7 @@ Parsed with #{link_to 'Gitlab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}. .pull-right - Attach images (JPG, PNG, GIF) by dragging & dropping + Attach files by dragging & dropping or #{link_to 'selecting them', '#', class: 'markdown-selector'}. .clearfix @@ -54,7 +54,7 @@ %span.light No open milestones available. - if can? current_user, :admin_milestone, @merge_request.target_project - = link_to 'Create new milestone', new_project_milestone_path(@merge_request.target_project), target: :blank + = link_to 'Create new milestone', new_namespace_project_milestone_path(@merge_request.target_project.namespace, @merge_request.target_project), target: :blank .form-group = f.label :label_ids, class: 'control-label' do %i.fa.fa-tag @@ -66,7 +66,7 @@ %span.light No labels yet. - if can? current_user, :admin_label, @merge_request.target_project - = link_to 'Create new label', new_project_label_path(@merge_request.target_project), target: :blank + = link_to 'Create new label', new_namespace_project_label_path(@merge_request.target_project.namespace, @merge_request.target_project), target: :blank .form-actions - if contribution_guide_url(@target_project) @@ -99,11 +99,11 @@ - if @diffs.present? = render "projects/diffs/diffs", diffs: @diffs, project: @project - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - .bs-callout.bs-callout-danger + .alert.alert-danger %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. %p To preserve performance the line changes are not shown. - else - .bs-callout.bs-callout-danger + .alert.alert-danger %h4 This comparison includes a huge diff. %p To preserve performance the line changes are not shown. @@ -113,10 +113,11 @@ e.preventDefault(); }); - window.project_image_path_upload = "#{upload_image_project_path @project}"; + window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; :javascript var merge_request merge_request = new MergeRequest({ action: 'commits' }); + diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 8e31a7e3fe4..ca4ceecb225 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,52 +1,53 @@ -.merge-request{'data-url' => project_merge_request_path(@project, @merge_request)} - = render "projects/merge_requests/show/mr_title" - %hr - = render "projects/merge_requests/show/mr_box" - %hr - .append-bottom-20 - .slead - %span From - - if @merge_request.for_fork? - %strong.label-branch< - - if @merge_request.source_project - = link_to @merge_request.source_project_namespace, project_path(@merge_request.source_project) - - else - \ #{@merge_request.source_project_namespace} - \:#{@merge_request.source_branch} - %span into - %strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch} - - else - %strong.label-branch #{@merge_request.source_branch} - %span into - %strong.label-branch #{@merge_request.target_branch} - - if @merge_request.open? - %span.pull-right - .btn-group - %a.btn.dropdown-toggle{ data: {toggle: :dropdown} } - %i.fa.fa-download - Download as - %span.caret - %ul.dropdown-menu - %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) - %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) +.merge-request{'data-url' => merge_request_path(@merge_request)} + .merge-request-details + = render "projects/merge_requests/show/mr_title" + %hr + = render "projects/merge_requests/show/mr_box" + %hr + .append-bottom-20 + .slead + %span From + - if @merge_request.for_fork? + %strong.label-branch< + - if @merge_request.source_project + = link_to @merge_request.source_project_namespace, namespace_project_path(@merge_request.source_project.namespace, @merge_request.source_project) + - else + \ #{@merge_request.source_project_namespace} + \:#{@merge_request.source_branch} + %span into + %strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch} + - else + %strong.label-branch #{@merge_request.source_branch} + %span into + %strong.label-branch #{@merge_request.target_branch} + - if @merge_request.open? + %span.pull-right + .btn-group + %a.btn.dropdown-toggle{ data: {toggle: :dropdown} } + %i.fa.fa-download + Download as + %span.caret + %ul.dropdown-menu + %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) + %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) - = render "projects/merge_requests/show/how_to_merge" - = render "projects/merge_requests/show/state_widget" + = render "projects/merge_requests/show/how_to_merge" + = render "projects/merge_requests/show/state_widget" - if @commits.present? %ul.nav.nav-tabs.merge-request-tabs %li.notes-tab{data: {action: 'notes'}} - = link_to project_merge_request_path(@project, @merge_request) do + = link_to merge_request_path(@merge_request) do %i.fa.fa-comments Discussion %span.badge= @merge_request.mr_and_commit_notes.count %li.commits-tab{data: {action: 'commits'}} - = link_to project_merge_request_path(@project, @merge_request), title: 'Commits' do + = link_to merge_request_path(@merge_request), title: 'Commits' do %i.fa.fa-history Commits %span.badge= @commits.size %li.diffs-tab{data: {action: 'diffs'}} - = link_to diffs_project_merge_request_path(@project, @merge_request) do + = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) do %i.fa.fa-list-alt Changes %span.badge= @merge_request.diffs.size @@ -67,9 +68,9 @@ var merge_request; merge_request = new MergeRequest({ - url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}", + url_to_automerge_check: "#{automerge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", check_enable: #{@merge_request.unchecked? ? "true" : "false"}, - url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}", + url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", ci_enable: #{@project.ci_service ? "true" : "false"}, current_status: "#{@merge_request.merge_status_name}", action: "#{controller.action_name}" diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 2654ea70990..d7992bdd19e 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,19 +1,11 @@ -= render "projects/issues_nav" +.append-bottom-10 + .pull-right + = render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project) + - if can? current_user, :write_merge_request, @project + = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new pull-left", title: "New Merge Request" do + %i.fa.fa-plus + New Merge Request + = render 'shared/issuable_filter' .merge-requests-holder - .append-bottom-10 - = render 'shared/issuable_filter' - .panel.panel-default - %ul.well-list.mr-list - = render @merge_requests - - if @merge_requests.blank? - %li - .nothing-here-block No merge requests to show - - if @merge_requests.present? - .pull-right - %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter - - = paginate @merge_requests, theme: "gitlab" - -:javascript - $(merge_requestsPage); + = render 'merge_requests' diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml index 21718ca2acf..a74f3fb24e7 100644 --- a/app/views/projects/merge_requests/show/_context.html.haml +++ b/app/views/projects/merge_requests/show/_context.html.haml @@ -1,23 +1,30 @@ -= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| %div.prepend-top-20 - %p - Assignee: + .issuable-context-title + %label + Assignee: - if @merge_request.assignee - = link_to_member(@project, @merge_request.assignee) + %strong= link_to_member(@project, @merge_request.assignee, size: 24) - else none - - if can?(current_user, :modify_merge_request, @merge_request) - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) + .issuable-context-selectbox + - if can?(current_user, :modify_merge_request, @merge_request) + = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) - %div.prepend-top-20 - %p - Milestone: + %div.prepend-top-20.clearfix + .issuable-context-title + %label + Milestone: - if @merge_request.milestone %span.back-to-milestone - #{link_to @merge_request.milestone.title, project_milestone_path(@project, @merge_request.milestone)} + = link_to namespace_project_milestone_path(@project.namespace, @project, @merge_request.milestone) do + %strong + %i.fa.fa-clock-o + = @merge_request.milestone.title - else none - - if can?(current_user, :modify_merge_request, @merge_request) - = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) - = hidden_field_tag :merge_request_context - = f.submit class: 'btn' + .issuable-context-selectbox + - if can?(current_user, :modify_merge_request, @merge_request) + = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) + = hidden_field_tag :merge_request_context + = f.submit class: 'btn' diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml index d361c5f579a..786b5f39063 100644 --- a/app/views/projects/merge_requests/show/_diffs.html.haml +++ b/app/views/projects/merge_requests/show/_diffs.html.haml @@ -3,10 +3,10 @@ - elsif @merge_request_diff.empty? .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} - else - .bs-callout.bs-callout-warning + .alert.alert-warning %h4 Changes view for this comparison is extremely large. %p You can - = link_to "download it", project_merge_request_path(@merge_request.target_project, @merge_request, format: :diff), class: "vlink" + = link_to "download it", merge_request_path(@merge_request, format: :diff), class: "vlink" instead. diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml index f8ee6973637..fb2c3220b8a 100644 --- a/app/views/projects/merge_requests/show/_mr_accept.html.haml +++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml @@ -12,12 +12,12 @@ - if @show_merge_controls .automerge_widget.can_be_merged.hide .clearfix - = form_for [:automerge, @project, @merge_request], remote: true, method: :post do |f| + = form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post do |f| .accept-merge-holder.clearfix.js-toggle-container .accept-action = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request" - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork? - .accept-control + .accept-control.checkbox = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do = check_box_tag :should_remove_source_branch Remove source-branch diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index ab1284547ad..ada9ae58b8f 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -1,4 +1,4 @@ -%h3.issue-title +%h2.issue-title = gfm escape_once(@merge_request.title) %div diff --git a/app/views/projects/merge_requests/show/_mr_ci.html.haml b/app/views/projects/merge_requests/show/_mr_ci.html.haml index ee7fd0ef157..85a7103f3bc 100644 --- a/app/views/projects/merge_requests/show/_mr_ci.html.haml +++ b/app/views/projects/merge_requests/show/_mr_ci.html.haml @@ -3,21 +3,21 @@ %i.fa.fa-check %span CI build passed for #{@merge_request.last_commit_short_sha}. - = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" .ci_widget.ci-failed{style: "display:none"} %i.fa.fa-times %span CI build failed for #{@merge_request.last_commit_short_sha}. - = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" - [:running, :pending].each do |status| .ci_widget{class: "ci-#{status}", style: "display:none"} %i.fa.fa-clock-o %span CI build #{status} for #{@merge_request.last_commit_short_sha}. - = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" .ci_widget %i.fa.fa-spinner diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 0f20eba382c..46e92a9c558 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -14,9 +14,9 @@ .issue-btn-group.pull-right - if can?(current_user, :modify_merge_request, @merge_request) - if @merge_request.open? - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-grouped btn-close", title: "Close merge request" - = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn btn-grouped issuable-edit", id: "edit_merge_request" do + = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-grouped btn-close", title: "Close merge request" + = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "btn btn-grouped issuable-edit", id: "edit_merge_request" do %i.fa.fa-pencil-square-o Edit - if @merge_request.closed? - = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request" + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request" diff --git a/app/views/projects/merge_requests/show/_participants.html.haml b/app/views/projects/merge_requests/show/_participants.html.haml index 15a97404cb0..4f34af1737d 100644 --- a/app/views/projects/merge_requests/show/_participants.html.haml +++ b/app/views/projects/merge_requests/show/_participants.html.haml @@ -1,4 +1,4 @@ .participants - %cite.cgray #{@merge_request.participants.count} participants + %span #{@merge_request.participants.count} participants - @merge_request.participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) diff --git a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml b/app/views/projects/merge_requests/show/_remove_source_branch.html.haml index 9bf6a9d081c..0a642b7e6d0 100644 --- a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml +++ b/app/views/projects/merge_requests/show/_remove_source_branch.html.haml @@ -4,7 +4,7 @@ - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && @merge_request.merged? .remove_source_branch_widget %p Changes merged into #{@merge_request.target_branch}. You can remove source branch now - = link_to project_branch_path(@merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-small remove_source_branch" do + = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-small remove_source_branch" do %i.fa.fa-times Remove Source Branch diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 0f51a347f01..95b7070ce5c 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -1,11 +1,11 @@ %h3.page-title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.iid}" .back-link - = link_to project_milestones_path(@project) do + = link_to namespace_project_milestones_path(@project.namespace, @project) do ← To milestones %hr -= form_for [@project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form'} do |f| += form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form'} do |f| -if @milestone.errors.any? .alert.alert-danger %ul @@ -21,11 +21,11 @@ .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 - = render layout: 'projects/md_preview' do + = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' .hint .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}. - .pull-left Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. + .pull-left Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. .clearfix .error-alert .col-md-6 @@ -38,10 +38,10 @@ .form-actions - if @milestone.new_record? = f.submit 'Create milestone', class: "btn-create btn" - = link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_milestones_path(@project.namespace, @project), class: "btn btn-cancel" -else = f.submit 'Save changes', class: "btn-save btn" - = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-cancel" :javascript @@ -51,4 +51,4 @@ onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val())); - window.project_image_path_upload = "#{upload_image_project_path @project}"; + window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/projects/milestones/_issue.html.haml index b5ec0fc9882..26c83841a22 100644 --- a/app/views/projects/milestones/_issue.html.haml +++ b/app/views/projects/milestones/_issue.html.haml @@ -1,8 +1,8 @@ -%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => project_issue_path(@project, issue) } +%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %span.str-truncated - = link_to [@project, issue] do + = link_to [@project.namespace.becomes(Namespace), @project, issue] do %span.cgray ##{issue.iid} - = link_to_gfm issue.title, [@project, issue], title: issue.title + = link_to_gfm issue.title, [@project.namespace.becomes(Namespace), @project, issue], title: issue.title .pull-right.assignee-icon - if issue.assignee = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16" diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/projects/milestones/_merge_request.html.haml index d54cb3f8e74..42fbd0cd2ca 100644 --- a/app/views/projects/milestones/_merge_request.html.haml +++ b/app/views/projects/milestones/_merge_request.html.haml @@ -1,5 +1,8 @@ -%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => project_merge_request_path(@project, merge_request) } +%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } %span.str-truncated - = link_to [@project, merge_request] do + = link_to [@project.namespace.becomes(Namespace), @project, merge_request] do %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [@project, merge_request], title: merge_request.title + = link_to_gfm merge_request.title, [@project.namespace.becomes(Namespace), @project, merge_request], title: merge_request.title + .pull-right.assignee-icon + - if merge_request.assignee + = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16" diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 1002b9513ff..dcf56541db8 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -1,12 +1,12 @@ %li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) } .pull-right - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link btn-grouped" do + = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-small edit-milestone-link btn-grouped" do %i.fa.fa-pencil-square-o Edit - = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-small btn-close" + = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-small btn-close" %h4 - = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone) + = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - if milestone.expired? and not milestone.closed? %span.cred (Expired) %small @@ -16,12 +16,11 @@ - else %div %div - = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do + = link_to namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do = pluralize milestone.issues.count, 'Issue' - = link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do + = link_to namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do = pluralize milestone.merge_requests.count, 'Merge Request' %span.light #{milestone.percent_complete}% complete - .progress.progress-info - .progress-bar{style: "width: #{milestone.percent_complete}%;"} + = milestone_progress_bar(milestone) diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index 04a1b9243d5..d3eab8d6d75 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,12 +1,8 @@ -= render "projects/issues_nav" -.milestones_content - %h3.page-title - Milestones - - if can? current_user, :admin_milestone, @project - = link_to new_project_milestone_path(@project), class: "pull-right btn btn-new", title: "New Milestone" do - %i.fa.fa-plus - New Milestone - +.pull-right + - if can? current_user, :admin_milestone, @project + = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do + %i.fa.fa-plus + New Milestone = render 'shared/milestones_filter' .milestones diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 031b5a31895..110d8967342 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,4 +1,3 @@ -= render "projects/issues_nav" %h4.page-title .issue-box{ class: issue_box_class(@milestone) } - if @milestone.closed? @@ -12,13 +11,13 @@ = @milestone.expires_at .pull-right - if can?(current_user, :admin_milestone, @project) - = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-grouped" do + = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o Edit - if @milestone.active? - = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" + = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" - else - = link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" + = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" %hr - if @milestone.issues.any? && @milestone.can_be_closed? @@ -44,8 +43,7 @@ %span.light #{@milestone.percent_complete}% complete %span.pull-right= @milestone.expires_at - .progress.progress-info - .progress-bar{style: "width: #{@milestone.percent_complete}%;"} + = milestone_progress_bar(@milestone) %ul.nav.nav-tabs @@ -63,10 +61,10 @@ %span.badge= @users.count .pull-right - = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do + = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do %i.fa.fa-plus New Issue - = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link btn-grouped" + = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link btn-grouped" .tab-content .tab-pane.active#tab-issues diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 4a21b84fb85..c36bad1e94b 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,7 +1,7 @@ = render "head" .project-network .controls - = form_tag project_network_path(@project, @id), method: :get, class: 'form-inline network-form' do |f| + = form_tag namespace_project_network_path(@project.namespace, @project, @id), method: :get, class: 'form-inline network-form' do |f| = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: 'search-input form-control input-mx-250 search-sha' = button_tag class: 'btn btn-success btn-search-sha' do %i.fa.fa-search @@ -18,8 +18,8 @@ disableButtonIfEmptyField('#extended_sha1', '.btn-search-sha') network_graph = new Network({ - url: '#{project_network_path(@project, @ref, @options.merge(format: :json))}', - commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}', + url: '#{namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))}', + commit_url: '#{namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")}', ref: '#{@ref}', commit_id: '#{@commit.id}' }) diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 61f6a66c386..00b912742b2 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -34,7 +34,7 @@ %span Import existing git repo .col-sm-10 = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .bs-callout.bs-callout-info + .alert.alert-info.prepend-top-10 This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. %br The import will time out after 4 minutes. For big repositories, use a clone/push combination. @@ -52,19 +52,40 @@ %i.fa.fa-github Import projects from GitHub = render 'github_import_modal' - + .project-import.form-group .col-sm-2 .col-sm-10 - - if gitlab_import_enabled? - = link_to status_import_gitlab_path do - %i.fa.fa-heart - Import projects from GitLab.com + - if bitbucket_import_enabled? + = link_to status_import_bitbucket_path do + %i.fa.fa-bitbucket + Import projects from Bitbucket - else = link_to '#', class: 'how_to_import_link light' do - %i.fa.fa-heart - Import projects from GitLab.com - = render 'gitlab_import_modal' + %i.fa.fa-bitbucket + Import projects from Bitbucket + = render 'bitbucket_import_modal' + + - unless request.host == 'gitlab.com' + .project-import.form-group + .col-sm-2 + .col-sm-10 + - if gitlab_import_enabled? + = link_to status_import_gitlab_path do + %i.fa.fa-heart + Import projects from GitLab.com + - else + = link_to '#', class: 'how_to_import_link light' do + %i.fa.fa-heart + Import projects from GitLab.com + = render 'gitlab_import_modal' + + .project-import.form-group + .col-sm-2 + .col-sm-10 + = link_to new_import_gitorious_path do + %i.icon-gitorious.icon-gitorious-small + Import projects from Gitorious.org %hr.prepend-botton-10 @@ -99,4 +120,4 @@ e.preventDefault() import_modal = $(this).parent().find(".modal").show() $('.modal-header .close').bind 'click', -> - $(".modal").hide()
\ No newline at end of file + $(".modal").hide() diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml index dd576243510..720957e8336 100644 --- a/app/views/projects/no_repo.html.haml +++ b/app/views/projects/no_repo.html.haml @@ -9,14 +9,14 @@ %hr .no-repo-actions - = link_to project_repository_path(@project), method: :post, class: 'btn btn-primary' do + = link_to namespace_project_repository_path(@project.namespace, @project), method: :post, class: 'btn btn-primary' do Create empty bare repository %strong.prepend-left-10.append-right-10 or - = link_to new_project_import_path(@project), class: 'btn' do + = link_to new_namespace_project_import_path(@project.namespace, @project), class: 'btn' do Import repository - if can? current_user, :remove_project, @project .prepend-top-20 - = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" + = link_to 'Remove project', project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index 59e2b3f1b0b..acb3991d294 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -1,22 +1,15 @@ .note-edit-form - = form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f| - = render layout: 'projects/md_preview' do + = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f| + = note_target_fields(note) + = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text' .comment-hints.clearfix .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }} - .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }. + .pull-right Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }. .note-form-actions .buttons = f.submit 'Save Comment', class: "btn btn-primary btn-save btn-grouped js-comment-button" - = link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel" - - .note-form-option.hidden-xs - %a.choose-btn.btn.js-choose-note-attachment-button - %i.fa.fa-paperclip - %span Choose File ... - - %span.file_name.js-attachment-filename - = f.file_field :attachment, class: "js-note-attachment-input hidden" + = link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
\ No newline at end of file diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 3879a0f10da..be96c302143 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -1,17 +1,17 @@ -= form_for [@project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f| - = note_target_fields += form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f| + = note_target_fields(@note) = f.hidden_field :commit_id = f.hidden_field :line_code = f.hidden_field :noteable_id = f.hidden_field :noteable_type - = render layout: 'projects/md_preview' do + = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text' .comment-hints.clearfix .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }} - .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }. + .pull-right Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }. .note-form-actions @@ -20,13 +20,5 @@ = yield(:note_actions) %a.btn.grouped.js-close-discussion-note-form Cancel - .note-form-option.hidden-xs - %a.choose-btn.btn.js-choose-note-attachment-button - %i.fa.fa-paperclip - %span Choose File ... - - %span.file_name.js-attachment-filename - = f.file_field :attachment, class: "js-note-attachment-input hidden" - :javascript - window.project_image_path_upload = "#{upload_image_project_path @project}"; + window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 88c7b7ccf1a..f3d00a6f06d 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -17,7 +17,7 @@ %i.fa.fa-pencil-square-o Edit - = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do + = link_to namespace_project_note_path(@project.namespace, @project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do %i.fa.fa-trash-o.cred Remove - if note.system @@ -57,13 +57,13 @@ - if note.attachment.url .note-attachment - if note.attachment.image? - = link_to note.attachment.secure_url, target: '_blank' do - = image_tag note.attachment.secure_url, class: 'note-image-attach' + = link_to note.attachment.url, target: '_blank' do + = image_tag note.attachment.url, class: 'note-image-attach' .attachment - = link_to note.attachment.secure_url, target: "_blank" do + = link_to note.attachment.url, target: "_blank" do %i.fa.fa-paperclip = note.attachment_identifier - = link_to delete_attachment_project_note_path(@project, note), + = link_to delete_attachment_namespace_project_note_path(@project.namespace, @project, note), title: "Delete this attachment", method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: "danger js-note-attachment-delete" do %i.fa.fa-trash-o.cred .clear diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml index 04ee17a40a0..813e37276bd 100644 --- a/app/views/projects/notes/_notes_with_form.html.haml +++ b/app/views/projects/notes/_notes_with_form.html.haml @@ -7,4 +7,4 @@ = render "projects/notes/form" :javascript - new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}) + new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}) diff --git a/app/views/projects/notes/discussions/_active.html.haml b/app/views/projects/notes/discussions/_active.html.haml index 52c06ec172d..7c6f7243173 100644 --- a/app/views/projects/notes/discussions/_active.html.haml +++ b/app/views/projects/notes/discussions/_active.html.haml @@ -8,7 +8,7 @@ %div = link_to_member(@project, note.author, avatar: false) started a discussion - = link_to diffs_project_merge_request_path(note.project, note.noteable, anchor: note.line_code) do + = link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do %strong on the diff .last-update.hide.js-toggle-content - last_note = discussion_notes.last diff --git a/app/views/projects/notes/discussions/_commit.html.haml b/app/views/projects/notes/discussions/_commit.html.haml index 94f16a5f02e..62609cfc1c8 100644 --- a/app/views/projects/notes/discussions/_commit.html.haml +++ b/app/views/projects/notes/discussions/_commit.html.haml @@ -8,7 +8,7 @@ %div = link_to_member(@project, note.author, avatar: false) started a discussion on commit - = link_to(note.noteable.short_id, project_commit_path(note.project, note.noteable), class: 'monospace') + = link_to(note.noteable.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace') .last-update.hide.js-toggle-content - last_note = discussion_notes.last last updated by diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml index e422799f55c..5406b80dc16 100644 --- a/app/views/projects/protected_branches/_branches_list.html.haml +++ b/app/views/projects/protected_branches/_branches_list.html.haml @@ -11,10 +11,10 @@ %tbody - @branches.each do |branch| - - @url = project_protected_branch_path(@project, branch) + - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch) %tr %td - = link_to project_commits_path(@project, branch.name) do + = link_to namespace_project_commits_path(@project.namespace, @project, branch.name) do %strong= branch.name - if @project.root_ref?(branch.name) %span.label.label-info default @@ -22,7 +22,7 @@ = check_box_tag "developers_can_push", branch.id, branch.developers_can_push, "data-url" => @url %td - if commit = branch.commit - = link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do + = link_to namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id' do = commit.short_id · #{time_ago_with_tooltip(commit.committed_date)} @@ -31,4 +31,4 @@ %td .pull-right - if can? current_user, :admin_project, @project - = link_to 'Unprotect', [@project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-small" + = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-small" diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml index 2164c874c74..cfe28084170 100644 --- a/app/views/projects/protected_branches/index.html.haml +++ b/app/views/projects/protected_branches/index.html.haml @@ -2,7 +2,7 @@ %p.light Keep stable branches secure and force developers to use Merge Requests %hr -.bs-callout.bs-callout-info +.alert.alert-info %p Protected branches are designed to %ul %li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"} @@ -11,7 +11,7 @@ %p Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"} - if can? current_user, :admin_project, @project - = form_for [@project, @protected_branch], html: { class: 'form-horizontal' } do |f| + = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'form-horizontal' } do |f| -if @protected_branch.errors.any? .alert.alert-danger %ul diff --git a/app/views/projects/refs/logs_tree.js.haml b/app/views/projects/refs/logs_tree.js.haml index 948a21aa816..49ce6c0888e 100644 --- a/app/views/projects/refs/logs_tree.js.haml +++ b/app/views/projects/refs/logs_tree.js.haml @@ -11,9 +11,9 @@ - if @logs.present? :plain var current_url = location.href.replace(/\/?$/, '/'); - var log_url = '#{project_tree_url(@project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/'); + var log_url = '#{namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/'); if(current_url == log_url) { // Load 10 more commit log for each file in tree // if we still on the same page - ajaxGet('#{logs_file_project_ref_path(@project, @ref, @path || '/', offset: (@offset + @limit))}'); + ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '/', offset: (@offset + @limit))}'); } diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index ce69adeb48c..26669fb00a9 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -3,7 +3,7 @@ - split_button = split_button || false - if split_button == true %span.btn-group{class: btn_class} - = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do %i.fa.fa-download %span Download zip %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } @@ -12,26 +12,26 @@ Select Archive Format %ul.dropdown-menu{ role: 'menu' } %li - = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), rel: 'nofollow' do %i.fa.fa-download %span Download zip %li - = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %i.fa.fa-download %span Download tar.gz %li - = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do %i.fa.fa-download %span Download tar.bz2 %li - = link_to archive_project_repository_path(@project, ref: ref, format: 'tar'), rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do %i.fa.fa-download %span Download tar - else %span.btn-group{class: btn_class} - = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do %i.fa.fa-download %span zip - = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do %i.fa.fa-download %span tar.gz diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml index c77ffff43fe..f3526ad0747 100644 --- a/app/views/projects/repositories/_feed.html.haml +++ b/app/views/projects/repositories/_feed.html.haml @@ -1,7 +1,7 @@ - commit = update %tr %td - = link_to project_commits_path(@project, commit.head.name) do + = link_to namespace_project_commits_path(@project.namespace, @project, commit.head.name) do %strong = commit.head.name - if @project.root_ref?(commit.head.name) @@ -9,7 +9,7 @@ %td %div - = link_to project_commits_path(@project, commit.id) do + = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do %code= commit.short_id = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' = gfm escape_once(truncate(commit.title, length: 40)) diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index ba270880881..3492dd5babd 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -5,12 +5,12 @@ %p= @service.description .back-link - = link_to project_services_path(@project) do + = link_to namespace_project_services_path(@project.namespace, @project) do ← to services %hr -= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f| += form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f| - if @service.errors.any? .alert.alert-danger %ul @@ -18,7 +18,7 @@ %li= msg - if @service.help.present? - .bs-callout + .alert.alert-info = preserve do = markdown @service.help @@ -27,16 +27,63 @@ .col-sm-10 = f.check_box :active + .form-group + = f.label :url, "Trigger", class: 'control-label' + - if @service.supported_events.length > 1 + .col-sm-10 + - if @service.supported_events.include?("push") + %div + = f.check_box :push_events, class: 'pull-left' + .prepend-left-20 + = f.label :push_events, class: 'list-label' do + %strong Push events + %p.light + This url will be triggered by a push to the repository + - if @service.supported_events.include?("tag_push") + %div + = f.check_box :tag_push_events, class: 'pull-left' + .prepend-left-20 + = f.label :tag_push_events, class: 'list-label' do + %strong Tag push events + %p.light + This url will be triggered when a new tag is pushed to the repository + - if @service.supported_events.include?("note") + %div + = f.check_box :note_events, class: 'pull-left' + .prepend-left-20 + = f.label :note_events, class: 'list-label' do + %strong Comments + %p.light + This url will be triggered when someone adds a comment + - if @service.supported_events.include?("issue") + %div + = f.check_box :issues_events, class: 'pull-left' + .prepend-left-20 + = f.label :issues_events, class: 'list-label' do + %strong Issues events + %p.light + This url will be triggered when an issue is created + - if @service.supported_events.include?("merge_request") + %div + = f.check_box :merge_requests_events, class: 'pull-left' + .prepend-left-20 + = f.label :merge_requests_events, class: 'list-label' do + %strong Merge Request events + %p.light + This url will be triggered when a merge request is created + - @service.fields.each do |field| - name = field[:name] - - value = @service.send(name) unless field[:type] == 'password' + - title = field[:title] || name.humanize + - value = service_field_value(field[:type], @service.send(name)) - type = field[:type] - placeholder = field[:placeholder] - choices = field[:choices] - default_choice = field[:default_choice] + - help = field[:help] .form-group - = f.label name, class: "control-label" + = f.label name, title, class: "control-label" .col-sm-10 - if type == 'text' = f.text_field name, class: "form-control", placeholder: placeholder @@ -47,10 +94,12 @@ - elsif type == 'select' = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } - elsif type == 'password' - = f.password_field name, class: 'form-control' + = f.password_field name, placeholder: value, class: 'form-control' + - if help + %span.help-block= help .form-actions = f.submit 'Save', class: 'btn btn-save' - if @service.valid? && @service.activated? && @service.can_test? - = link_to 'Test settings', test_project_service_path(@project, @service.to_param), class: 'btn' + = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: 'btn' diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml index 4604c0afd8d..0d3ccb6bb83 100644 --- a/app/views/projects/services/index.html.haml +++ b/app/views/projects/services/index.html.haml @@ -6,14 +6,14 @@ %tr %th %th Service - %th Desription + %th Description %th Last edit - @services.sort_by(&:title).each do |service| %tr %td = boolean_to_icon service.activated? %td - = link_to edit_project_service_path(@project, service.to_param) do + = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do %strong= service.title %td = service.description diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 737a34decde..74b07395650 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,5 +1,6 @@ - if current_user && can?(current_user, :download_code, @project) = render 'shared/no_ssh' + = render 'shared/no_password' = render "home_panel" @@ -14,20 +15,22 @@ Readme .project-home-links - unless @project.empty_repo? - = link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), project_commits_path(@project, @ref || @repository.root_ref) - = link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), project_branches_path(@project) - = link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), project_tags_path(@project) + = link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) + = link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), namespace_project_branches_path(@project.namespace, @project) + = link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), namespace_project_tags_path(@project.namespace, @project) %span.light.prepend-left-20= repository_size .tab-content .tab-pane.active#tab-activity .row + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left %section.col-md-9 = render "events/event_last_push", event: @last_push = render 'shared/event_filter' .content_list = spinner - %aside.col-md-3.project-side.hidden-sm.hidden-xs + %aside.col-md-3.project-side .clearfix - if @project.archived? .alert.alert-warning @@ -37,19 +40,19 @@ %p Repository is read-only - if @project.forked_from_project - .alert.alert-success + .well %i.fa.fa-code-fork.project-fork-icon Forked from: %br - = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) + = link_to @project.forked_from_project.name_with_namespace, namespace_project_path(@project.namespace, @project.forked_from_project) - unless @project.empty_repo? - = link_to project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do + = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do Compare code - if @repository.version - version = @repository.version - = link_to project_blob_path(@project, tree_join(@repository.root_ref, version.name)), class: 'btn btn-block' do + = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, version.name)), class: 'btn btn-block' do Version: %span.count = @repository.blob_by_oid(version.id).data @@ -77,7 +80,7 @@ - if readme .tab-pane#tab-readme %article.readme-holder#README - = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do + = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do %h4.readme-file-title %i.fa.fa-file = readme.name diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index f6a5bf9e4ff..2d4d5d030ab 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -1,4 +1,4 @@ %h3.page-title Edit snippet %hr -= render "shared/snippets/form", url: project_snippet_path(@project, @snippet) += render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet) diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index e60f9a44322..e2d8ec673a1 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,7 +1,7 @@ %h3.page-title Snippets - if can? current_user, :write_project_snippet, @project - = link_to new_project_snippet_path(@project), class: "btn btn-new pull-right", title: "New Snippet" do + = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Snippet" do Add new snippet %p.light diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index 10f684b6316..bb659dba0cf 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -1,4 +1,4 @@ %h3.page-title New snippet %hr -= render "shared/snippets/form", url: project_snippets_path(@project, @snippet) += render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet) diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index ada0d30c496..345848fa6d1 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -2,7 +2,7 @@ = @snippet.title .pull-right - = link_to new_project_snippet_path(@project), class: "btn btn-new", title: "New Snippet" do + = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do Add new snippet %hr @@ -17,7 +17,7 @@ = @snippet.author_name .back-link - = link_to project_snippets_path(@project) do + = link_to namespace_project_snippets_path(@project.namespace, @project) do ← project snippets .file-holder @@ -28,10 +28,10 @@ .options .btn-group - if can?(current_user, :modify_project_snippet, @snippet) - = link_to "edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small", title: 'Edit Snippet' - = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-small", target: "_blank" + = link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-small", title: 'Edit Snippet' + = link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-small", target: "_blank" - if can?(current_user, :admin_project_snippet, @snippet) - = link_to "remove", project_snippet_path(@project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-small btn-remove", title: 'Delete Snippet' + = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-small btn-remove", title: 'Delete Snippet' = render 'shared/snippets/blob' %div#notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 4ab102ba96c..8da07222cba 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -1,7 +1,7 @@ - commit = @repository.commit(tag.target) %li %h4 - = link_to project_commits_path(@project, tag.name), class: "" do + = link_to namespace_project_commits_path(@project.namespace, @project, tag.name), class: "" do %i.fa.fa-tag = tag.name - if tag.message.present? @@ -11,7 +11,7 @@ - if can? current_user, :download_code, @project = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-small' - if can?(current_user, :admin_project, @project) - = link_to project_tag_path(@project, tag.name), class: 'btn btn-small btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do + = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-small btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do %i.fa.fa-trash-o - if commit diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index ac74e3b6d36..f1bc2bc9a2b 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -4,7 +4,7 @@ Git Tags - if can? current_user, :push_code, @project .pull-right - = link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do + = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do %i.fa.fa-add-sign New tag diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 289c52a2e3f..655044438d5 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -5,7 +5,7 @@ %h3.page-title %i.fa.fa-code-fork New tag -= form_tag project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do += form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do .form-group = label_tag :tag_name, 'Name for new tag', class: 'control-label' .col-sm-10 @@ -22,7 +22,7 @@ .light (Optional) Entering a message will create an annotated tag. .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 - = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' + = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript disableButtonIfAnyEmptyField($("#new-tag-form"), ".form-control", ".btn-create"); diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml index ddf8cb76f78..166b6362a07 100644 --- a/app/views/projects/team_members/_form.html.haml +++ b/app/views/projects/team_members/_form.html.haml @@ -1,7 +1,7 @@ %h3.page-title New project member(s) -= form_for @user_project_relation, as: :project_member, url: project_team_members_path(@project), html: { class: "form-horizontal users-project-form" } do |f| += form_for @user_project_relation, as: :project_member, url: namespace_project_team_members_path(@project.namespace, @project), html: { class: "form-horizontal users-project-form" } do |f| -if @user_project_relation.errors.any? .alert.alert-danger %ul @@ -26,4 +26,4 @@ .form-actions = f.submit 'Add users', class: "btn btn-create" - = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml index 7a9c0939ba0..eb815447407 100644 --- a/app/views/projects/team_members/_team_member.html.haml +++ b/app/views/projects/team_members/_team_member.html.haml @@ -4,14 +4,15 @@ - if current_user_can_admin_project - unless @project.personal? && user == current_user .pull-left - = form_for(member, as: :project_member, url: project_team_member_path(@project, member.user)) do |f| + = form_for(member, as: :project_member, url: namespace_project_team_member_path(@project.namespace, @project, member.user)) do |f| = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: "trigger-submit" - = link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do + = link_to namespace_project_team_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do %i.fa.fa-minus.fa-inverse = image_tag avatar_icon(user.email, 32), class: "avatar s32" %p %strong= user.name + - if user.blocked? + %label.label.label-danger + %strong Blocked %span.cgray= user.username - - diff --git a/app/views/projects/team_members/import.html.haml b/app/views/projects/team_members/import.html.haml index d1f46c61b2e..9e31d47117e 100644 --- a/app/views/projects/team_members/import.html.haml +++ b/app/views/projects/team_members/import.html.haml @@ -3,12 +3,12 @@ %p.light Only project members will be imported. Group members will be skipped. %hr -= form_tag apply_import_project_team_members_path(@project), method: 'post', class: 'form-horizontal' do += form_tag apply_import_namespace_project_team_members_path(@project.namespace, @project), method: 'post', class: 'form-horizontal' do .form-group = label_tag :source_project_id, "Project", class: 'control-label' .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .form-actions = button_tag 'Import project members', class: "btn btn-create" - = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/team_members/index.html.haml b/app/views/projects/team_members/index.html.haml index ecb7c689e8a..fcc879a58df 100644 --- a/app/views/projects/team_members/index.html.haml +++ b/app/views/projects/team_members/index.html.haml @@ -3,9 +3,9 @@ - if can? current_user, :admin_team_member, @project %span.pull-right - = link_to new_project_team_member_path(@project), class: "btn btn-new btn-grouped", title: "New project member" do + = link_to new_namespace_project_team_member_path(@project.namespace, @project), class: "btn btn-new btn-grouped", title: "New project member" do New project member - = link_to import_project_team_members_path(@project), class: "btn btn-grouped", title: "Import members from another project" do + = link_to import_namespace_project_team_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do Import members %p.light diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml index 6d083c5c516..17b9fecfeb1 100644 --- a/app/views/projects/transfer.js.haml +++ b/app/views/projects/transfer.js.haml @@ -1,2 +1,2 @@ :plain - location.href = "#{edit_project_path(@project)}"; + location.href = "#{edit_namespace_project_path(@project.namespace, @project)}"; diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml index 393ef0e24bd..b253fe896e3 100644 --- a/app/views/projects/tree/_blob_item.html.haml +++ b/app/views/projects/tree/_blob_item.html.haml @@ -2,7 +2,7 @@ %td.tree-item-file-name = tree_icon(type) %span.str-truncated - = link_to blob_item.name, project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)) + = link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name)) %td.tree_time_ago.cgray = render 'spinner' %td.hidden-xs.tree_commit diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml index f902440b3f1..d304690d162 100644 --- a/app/views/projects/tree/_tree.html.haml +++ b/app/views/projects/tree/_tree.html.haml @@ -1,16 +1,16 @@ %ul.breadcrumb.repo-breadcrumb %li - = link_to project_tree_path(@project, @ref) do + = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do = @project.path - tree_breadcrumbs(tree, 6) do |title, path| %li - if path - = link_to truncate(title, length: 40), project_tree_path(@project, path) + = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) - else = link_to title, '#' - if current_user && can_push_branch?(@project, @ref) %li - = link_to project_new_blob_path(@project, @id), title: 'New file', id: 'new-file-link' do + = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'New file', id: 'new-file-link' do %small %i.fa.fa-plus @@ -27,15 +27,15 @@ %i.fa.fa-angle-right %small.light - = link_to @commit.short_id, project_commit_path(@project, @commit) + = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit) – = truncate(@commit.title, length: 50) - = link_to 'History', project_commits_path(@project, @id), class: 'pull-right' + = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right' - if @path.present? %tr.tree-item %td.tree-item-file-name - = link_to "..", project_tree_path(@project, up_dir_path), class: 'prepend-left-10' + = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10' %td %td.hidden-xs diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml index bd50dd4d9a2..50521264a61 100644 --- a/app/views/projects/tree/_tree_commit_column.html.haml +++ b/app/views/projects/tree/_tree_commit_column.html.haml @@ -1,3 +1,3 @@ %span.str-truncated %span.tree_author= commit_author_link(commit, avatar: true, size: 16) - = link_to_gfm commit.title, project_commit_path(@project, commit.id), class: "tree-commit-link" + = link_to_gfm commit.title, namespace_project_commit_path(@project.namespace, @project, commit.id), class: "tree-commit-link" diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml index 5adbf93ff8f..94342bc9b2b 100644 --- a/app/views/projects/tree/_tree_item.html.haml +++ b/app/views/projects/tree/_tree_item.html.haml @@ -3,7 +3,7 @@ = tree_icon(type) %span.str-truncated - path = flatten_tree(tree_item) - = link_to path, project_tree_path(@project, tree_join(@id || @commit.id, path)) + = link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path)) %td.tree_time_ago.cgray = render 'spinner' %td.hidden-xs.tree_commit diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml index cbb21f2b9fb..4f3f4cab8d5 100644 --- a/app/views/projects/update.js.haml +++ b/app/views/projects/update.js.haml @@ -1,6 +1,6 @@ - if @project.valid? :plain - location.href = "#{edit_project_path(@project)}"; + location.href = "#{edit_namespace_project_path(@project.namespace, @project)}"; - else :plain $(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 111484c8316..9fbfa0b1aeb 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f| -if @page.errors.any? #error_explanation .alert.alert-danger @@ -22,11 +22,11 @@ .form-group.wiki-content = f.label :content, class: 'control-label' .col-sm-10 - = render layout: 'projects/md_preview' do + = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do = render 'projects/zen', f: f, attr: :content, classes: 'description form-control' .col-sm-12.hint .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'} - .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. + .pull-right Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. .clearfix .error-alert @@ -37,11 +37,10 @@ .form-actions - if @page && @page.persisted? = f.submit 'Save changes', class: "btn-save btn" - = link_to "Cancel", project_wiki_path(@project, @page), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-cancel" - else = f.submit 'Create page', class: "btn-create btn" - = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel" :javascript - window.project_image_path_upload = "#{upload_image_project_path @project}"; - + window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}"; diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml index 30410bc95e0..633214a4e86 100644 --- a/app/views/projects/wikis/_main_links.html.haml +++ b/app/views/projects/wikis/_main_links.html.haml @@ -1,8 +1,8 @@ %span.pull-right - if (@page && @page.persisted?) - = link_to history_project_wiki_path(@project, @page), class: "btn btn-grouped" do + = link_to history_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do Page History - if can?(current_user, :write_wiki, @project) - = link_to edit_project_wiki_path(@project, @page), class: "btn btn-grouped" do + = link_to edit_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o Edit diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml index 90539fde583..693c3facb32 100644 --- a/app/views/projects/wikis/_nav.html.haml +++ b/app/views/projects/wikis/_nav.html.haml @@ -1,12 +1,12 @@ %ul.nav.nav-tabs = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do - = link_to 'Home', project_wiki_path(@project, :home) + = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home) = nav_link(path: 'wikis#pages') do - = link_to 'Pages', pages_project_wikis_path(@project) + = link_to 'Pages', pages_namespace_project_wikis_path(@project.namespace, @project) = nav_link(path: 'wikis#git_access') do - = link_to git_access_project_wikis_path(@project) do + = link_to git_access_namespace_project_wikis_path(@project.namespace, @project) do %i.fa.fa-download Git Access diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index 1ce292a02df..6834969de8b 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -7,7 +7,7 @@ .modal-body = label_tag :new_wiki_path do %span Page slug - = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project) + = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project) %p.hint Please don't use spaces. .modal-footer diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 5347caf000a..5567f1af22a 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -9,5 +9,5 @@ .pull-right - if @page.persisted? && can?(current_user, :admin_wiki, @project) - = link_to project_wiki_path(@project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-small btn-remove" do + = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-small btn-remove" do Delete this page diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml index 9c9a9933dcf..91291f753f7 100644 --- a/app/views/projects/wikis/history.html.haml +++ b/app/views/projects/wikis/history.html.haml @@ -1,7 +1,7 @@ = render 'nav' %h3.page-title %span.light History for - = link_to @page.title, project_wiki_path(@project, @page) + = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page) %table.table %thead diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 264b48ec36c..ee233d9086f 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -5,7 +5,7 @@ - @wiki_pages.each do |wiki_page| %li %h4 - = link_to wiki_page.title, project_wiki_path(@project, wiki_page) + = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) %small (#{wiki_page.format}) .pull-right %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index ede4fef9e24..a6263e93f67 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -5,7 +5,7 @@ - if @page.historical? .warning_message This is an old version of this page. - You can view the #{link_to "most recent version", project_wiki_path(@project, @page)} or browse the #{link_to "history", history_project_wiki_path(@project, @page)}. + You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", history_namespace_project_wiki_path(@project.namespace, @project, @page)}. %hr diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml index eca69ce50b1..c635c04fb8f 100644 --- a/app/views/search/_filter.html.haml +++ b/app/views/search/_filter.html.haml @@ -1,5 +1,5 @@ .dropdown.inline - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle.btn.btn-small{type: 'button', 'data-toggle' => 'dropdown'} %i.fa.fa-tags %span.light Group: - if @group.present? @@ -17,7 +17,7 @@ = group.name .dropdown.inline.prepend-left-10.project-filter - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle.btn.btn-small{type: 'button', 'data-toggle' => 'dropdown'} %i.fa.fa-tags %span.light Project: - if @project.present? diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index 58bcff9dbe3..796dd752a4c 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -2,7 +2,7 @@ #{@search_results.total_count} results found - unless @show_snippets - if @project - for #{link_to @project.name_with_namespace, @project} + for #{link_to @project.name_with_namespace, [@project.namespace.becomes(Namespace), @project]} - elsif @group for #{link_to @group.name, @group} diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index dae641dab4f..84e9be82c44 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -1,7 +1,7 @@ .blob-result .file-holder .file-title - = link_to project_blob_path(@project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do + = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do %i.fa.fa-file %strong = blob.filename diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index 7868f958261..ce8ddff9556 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -1,6 +1,6 @@ .search-result-row %h4 - = link_to [issue.project, issue] do + = link_to [issue.project.namespace.becomes(Namespace), issue.project, issue] do %span.term.str-truncated= issue.title .pull-right ##{issue.iid} - if issue.description.present? diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index 56b185283bd..2efa616d664 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -1,6 +1,6 @@ .search-result-row %h4 - = link_to [merge_request.target_project, merge_request] do + = link_to [merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request] do %span.term.str-truncated= merge_request.title .pull-right ##{merge_request.iid} - if merge_request.description.present? diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml index a44a4542df5..5fcba2b7e93 100644 --- a/app/views/search/results/_note.html.haml +++ b/app/views/search/results/_note.html.haml @@ -9,7 +9,7 @@ = link_to project do = project.name_with_namespace · - = link_to project_commit_path(project, note.commit_id, anchor: dom_id(note)) do + = link_to namespace_project_commit_path(project.namespace, project, note.commit_id, anchor: dom_id(note)) do Commit #{truncate_sha(note.commit_id)} - else = link_to project do @@ -17,7 +17,7 @@ · %span #{note.noteable_type.titleize} ##{note.noteable.iid} · - = link_to [project, note.noteable, anchor: dom_id(note)] do + = link_to [project.namespace.becomes(Namespace), project, note.noteable, anchor: dom_id(note)] do = note.noteable.title .note-search-result diff --git a/app/views/search/results/_project.html.haml b/app/views/search/results/_project.html.haml index 301b65eca29..195cf06c8ea 100644 --- a/app/views/search/results/_project.html.haml +++ b/app/views/search/results/_project.html.haml @@ -1,6 +1,6 @@ .search-result-row %h4 - = link_to project do + = link_to [project.namespace.becomes(Namespace), project] do %span.term= project.name_with_namespace - if project.description.present? %span.light.term= project.description diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml index f7e5ee5e20e..c414acb6a11 100644 --- a/app/views/search/results/_snippet_title.html.haml +++ b/app/views/search/results/_snippet_title.html.haml @@ -11,7 +11,7 @@ %small.pull-right.cgray - if snippet_title.project_id? - = link_to snippet_title.project.name_with_namespace, project_path(snippet_title.project) + = link_to snippet_title.project.name_with_namespace, namespace_project_path(snippet_title.project.namespace, snippet_title.project) .snippet-info = "##{snippet_title.id}" diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml index c7bc596eb14..f9c5810e3d0 100644 --- a/app/views/search/results/_wiki_blob.html.haml +++ b/app/views/search/results/_wiki_blob.html.haml @@ -1,7 +1,7 @@ .blob-result .file-holder .file-title - = link_to project_wiki_path(@project, wiki_blob.filename) do + = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.filename) do %i.fa.fa-file %strong = wiki_blob.filename diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 1cc6043f56b..a1121750ca3 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -1,8 +1,20 @@ - project = project || @project .git-clone-holder.input-group .input-group-btn - %button{class: "btn #{ 'active' if default_clone_protocol == 'ssh' }", :"data-clone" => project.ssh_url_to_repo} SSH - %button{class: "btn #{ 'active' if default_clone_protocol == 'http' }", :"data-clone" => project.http_url_to_repo}= gitlab_config.protocol.upcase + %button{ | + class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", | + :"data-clone" => project.ssh_url_to_repo, | + :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH", + :"data-html" => "true", + :"data-container" => "body"} + SSH + %button{ | + class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", | + :"data-clone" => project.http_url_to_repo, | + :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}", + :"data-html" => "true", + :"data-container" => "body"} + = gitlab_config.protocol.upcase = text_field_tag :project_clone, default_url_to_repo(project), class: "one_click_select form-control", readonly: true - if project.kind_of?(Project) .input-group-addon diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index 52b48ff7451..fba69dd0f3f 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -7,4 +7,5 @@ = link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do %i.fa.fa-link = i - = highlight(blob.name, blob.data) + :preserve + #{highlight(blob.name, blob.data)} diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index 5875f71bac2..b34dd53e3b5 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -15,7 +15,7 @@ = f.text_field :path, placeholder: 'open-source', class: 'form-control', autofocus: local_assigns[:autofocus] || false - if @group.persisted? - .bs-callout.bs-callout-danger + .alert.alert-danger %ul %li Changing group path can have unintended side effects. %li Renaming group path will rename directory for all related projects diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml index cd97481bb6c..5412b9ef0f4 100644 --- a/app/views/shared/_issuable_filter.html.haml +++ b/app/views/shared/_issuable_filter.html.haml @@ -1,6 +1,6 @@ .issues-filters - .pull-left.append-right-20 - %ul.nav.nav-pills.nav-compact + .issues-state-filters + %ul.nav.nav-tabs %li{class: ("active" if params[:state] == 'opened')} = link_to page_filter_path(state: 'opened') do %i.fa.fa-exclamation-circle @@ -14,99 +14,106 @@ %i.fa.fa-compass All - .dropdown.inline.assignee-filter - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-user - %span.light assignee: - - if @assignee.present? - %strong= @assignee.name - - elsif params[:assignee_id] == "0" - Unassigned - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(assignee_id: nil) do - Any - = link_to page_filter_path(assignee_id: 0) do - Unassigned - - @assignees.sort_by(&:name).each do |user| - %li - = link_to page_filter_path(assignee_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name - - .dropdown.inline.prepend-left-10.author-filter - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-user - %span.light author: - - if @author.present? - %strong= @author.name - - elsif params[:author_id] == "0" - Unassigned - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(author_id: nil) do - Any - = link_to page_filter_path(author_id: 0) do - Unassigned - - @authors.sort_by(&:name).each do |user| - %li - = link_to page_filter_path(author_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name - - .dropdown.inline.prepend-left-10.milestone-filter - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-clock-o - %span.light milestone: - - if @milestone.present? - %strong= @milestone.title - - elsif params[:milestone_id] == "0" - None (backlog) - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(milestone_id: nil) do - Any - = link_to page_filter_path(milestone_id: 0) do - None (backlog) - - @milestones.each do |milestone| - %li - = link_to page_filter_path(milestone_id: milestone.id) do - %strong= milestone.title - %small.light= milestone.expires_at + %div + - if controller.controller_name == 'issues' + .check-all-holder + = check_box_tag "check_all_issues", nil, false, + class: "check_all_issues left", + disabled: !can?(current_user, :modify_issue, @project) + .issues-other-filters + .dropdown.inline.assignee-filter + %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} + %i.fa.fa-user + %span.light assignee: + - if @assignee.present? + %strong= @assignee.name + - elsif params[:assignee_id] == "0" + Unassigned + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to page_filter_path(assignee_id: nil) do + Any + = link_to page_filter_path(assignee_id: 0) do + Unassigned + - @assignees.sort_by(&:name).each do |user| + %li + = link_to page_filter_path(assignee_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name - - if @project - .dropdown.inline.prepend-left-10.labels-filter - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-tags - %span.light label: - - if params[:label_name].present? - %strong= params[:label_name] - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(label_name: nil) do + .dropdown.inline.prepend-left-10.author-filter + %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} + %i.fa.fa-user + %span.light author: + - if @author.present? + %strong= @author.name + - elsif params[:author_id] == "0" + Unassigned + - else Any - - if @project.labels.any? - - @project.labels.each do |label| + %b.caret + %ul.dropdown-menu + %li + = link_to page_filter_path(author_id: nil) do + Any + = link_to page_filter_path(author_id: 0) do + Unassigned + - @authors.sort_by(&:name).each do |user| %li - = link_to page_filter_path(label_name: label.name) do - = render_colored_label(label) - - else + = link_to page_filter_path(author_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name + + .dropdown.inline.prepend-left-10.milestone-filter + %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} + %i.fa.fa-clock-o + %span.light milestone: + - if @milestone.present? + %strong= @milestone.title + - elsif params[:milestone_id] == "0" + None (backlog) + - else + Any + %b.caret + %ul.dropdown-menu %li - = link_to generate_project_labels_path(@project, redirect: request.original_url), method: :post do - %i.fa.fa-plus-circle - Create default labels + = link_to page_filter_path(milestone_id: nil) do + Any + = link_to page_filter_path(milestone_id: 0) do + None (backlog) + - @milestones.each do |milestone| + %li + = link_to page_filter_path(milestone_id: milestone.id) do + %strong= milestone.title + %small.light= milestone.expires_at + + - if @project + .dropdown.inline.prepend-left-10.labels-filter + %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} + %i.fa.fa-tags + %span.light label: + - if params[:label_name].present? + %strong= params[:label_name] + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to page_filter_path(label_name: nil) do + Any + - if @project.labels.any? + - @project.labels.each do |label| + %li + = link_to page_filter_path(label_name: label.name) do + = render_colored_label(label) + - else + %li + = link_to generate_namespace_project_labels_path(@project.namespace, @project, redirect: request.original_url), method: :post do + %i.fa.fa-plus-circle + Create default labels - .pull-right - = render 'shared/sort_dropdown' + .pull-right + = render 'shared/sort_dropdown' diff --git a/app/views/shared/_issuable_search_form.html.haml b/app/views/shared/_issuable_search_form.html.haml new file mode 100644 index 00000000000..639d203dcd6 --- /dev/null +++ b/app/views/shared/_issuable_search_form.html.haml @@ -0,0 +1,9 @@ += form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do + .append-right-10.hidden-xs.hidden-sm + = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } + = hidden_field_tag :state, params['state'] + = hidden_field_tag :scope, params['scope'] + = hidden_field_tag :assignee_id, params['assignee_id'] + = hidden_field_tag :author_id, params['author_id'] + = hidden_field_tag :milestone_id, params['milestone_id'] + = hidden_field_tag :label_id, params['label_id'] diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml index e976f897dc9..0dbb6a04393 100644 --- a/app/views/shared/_issues.html.haml +++ b/app/views/shared/_issues.html.haml @@ -4,7 +4,7 @@ - project = group[0] .panel-heading = link_to_project project - = link_to 'show all', project_issues_path(project), class: 'pull-right' + = link_to 'show all', namespace_project_issues_path(project.namespace, project), class: 'pull-right' %ul.well-list.issues-list - group[1].each do |issue| diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml index 39a1ee38f8e..c02c5af008a 100644 --- a/app/views/shared/_merge_requests.html.haml +++ b/app/views/shared/_merge_requests.html.haml @@ -4,7 +4,7 @@ - project = group[0] .panel-heading = link_to_project project - = link_to 'show all', project_merge_requests_path(project), class: 'pull-right' + = link_to 'show all', namespace_project_merge_requests_path(project.namespace, project), class: 'pull-right' %ul.well-list.mr-list - group[1].each do |merge_request| = render 'projects/merge_requests/merge_request', merge_request: merge_request diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml index 208f1b77372..f685ae7726c 100644 --- a/app/views/shared/_milestones_filter.html.haml +++ b/app/views/shared/_milestones_filter.html.haml @@ -1,5 +1,5 @@ .milestones-filters.append-bottom-10 - %ul.nav.nav-pills.nav-compact + %ul.nav.nav-tabs %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')} = link_to milestones_filter_path(state: 'opened') do %i.fa.fa-exclamation-circle diff --git a/app/views/shared/_no_password.html.haml b/app/views/shared/_no_password.html.haml new file mode 100644 index 00000000000..a43bf33751a --- /dev/null +++ b/app/views/shared/_no_password.html.haml @@ -0,0 +1,8 @@ +- if cookies[:hide_no_password_message].blank? && !current_user.hide_no_password && current_user.require_password? + .no-password-message.alert.alert-warning.hidden-xs + You won't be able to pull or push project code via #{gitlab_config.protocol.upcase} until you #{link_to 'set a password', edit_profile_password_path} on your account + + .pull-right + = link_to "Don't show again", profile_path(user: {hide_no_password: true}), method: :put + | + = link_to 'Remind later', '#', class: 'hide-no-password-message' diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml index 8e6f802fd3b..089179e677a 100644 --- a/app/views/shared/_no_ssh.html.haml +++ b/app/views/shared/_no_ssh.html.haml @@ -1,8 +1,8 @@ -- if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key? && !current_user.hide_no_ssh_key +- if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key? .no-ssh-key-message.alert.alert-warning.hidden-xs - You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile + You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path, class: 'alert-link'} to your profile .pull-right - = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put + = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link' | - = link_to 'Remind later', '#', class: 'hide-no-ssh-message' + = link_to 'Remind later', '#', class: 'hide-no-ssh-message alert-link' diff --git a/app/views/shared/_project.html.haml b/app/views/shared/_project.html.haml new file mode 100644 index 00000000000..8746970c239 --- /dev/null +++ b/app/views/shared/_project.html.haml @@ -0,0 +1,21 @@ += cache [project, controller.controller_name, controller.action_name] do + = link_to project_path(project), class: dom_class(project) do + - if avatar + .dash-project-avatar + = project_icon(project, alt: '', class: 'avatar project-avatar s40') + .dash-project-access-icon + = visibility_level_icon(project.visibility_level) + %span.str-truncated + %span.namespace-name + - if project.namespace + = project.namespace.human_name + \/ + %span.project-name.filter-title + = project.name + - if stars + %span.pull-right.light + %i.fa.fa-star + = project.star_count + - else + %span.arrow + %i.fa.fa-angle-right diff --git a/app/views/shared/_projects_list.html.haml b/app/views/shared/_projects_list.html.haml new file mode 100644 index 00000000000..4c58092af44 --- /dev/null +++ b/app/views/shared/_projects_list.html.haml @@ -0,0 +1,17 @@ +- projects_limit = 20 unless local_assigns[:projects_limit] +- avatar = true unless local_assigns[:avatar] == false +- stars = false unless local_assigns[:stars] == true +%ul.well-list.projects-list + - projects.each_with_index do |project, i| + %li{class: (i >= projects_limit) ? 'project-row hide' : 'project-row'} + = render "shared/project", project: project, avatar: avatar, stars: stars + - if projects.blank? + %li + .nothing-here-block There are no projects here. + - if projects.count > projects_limit + %li.bottom + %span.light + #{projects_limit} of #{pluralize(projects.count, 'project')} displayed. + %span + = link_to '#', class: 'js-expand' do + Show all diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml index 4d9534f49b1..eb2e1919e19 100644 --- a/app/views/shared/_ref_switcher.html.haml +++ b/app/views/shared/_ref_switcher.html.haml @@ -1,4 +1,4 @@ -= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do += form_tag switch_namespace_project_refs_path(@project.namespace, @project), method: :get, class: "project-refs-form" do = select_tag "ref", grouped_options_refs, class: "project-refs-select select2 select2-sm" = hidden_field_tag :destination, destination - if defined?(path) diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index ba14c8643cd..af3d35de325 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -1,5 +1,5 @@ .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light sort: - if @sort.present? = sort_options_hash[@sort] diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index f729f129e45..4e0663ea208 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -30,7 +30,7 @@ = f.submit 'Save', class: "btn-save btn" - if @snippet.respond_to?(:project) - = link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel" - else = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel" diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml index c584dd8dfb6..5bb28664349 100644 --- a/app/views/snippets/_snippet.html.haml +++ b/app/views/snippets/_snippet.html.haml @@ -11,7 +11,7 @@ %small.pull-right.cgray - if snippet.project_id? - = link_to snippet.project.name_with_namespace, project_path(snippet.project) + = link_to snippet.project.name_with_namespace, namespace_project_path(snippet.project.namespace, snippet.project) .snippet-info = "##{snippet.id}" diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml index cb84570a6d5..f360fbb3d5d 100644 --- a/app/views/users/_groups.html.haml +++ b/app/views/users/_groups.html.haml @@ -1,4 +1,4 @@ .clearfix - groups.each do |group| = link_to group, class: 'profile-groups-avatars inline', title: group.name do - = image_tag group_icon(group.path), class: 'avatar group-avatar s40' + = image_tag group_icon(group), class: 'avatar group-avatar s40' diff --git a/app/views/users/_profile.html.haml b/app/views/users/_profile.html.haml index 3b44959baad..0a70b738071 100644 --- a/app/views/users/_profile.html.haml +++ b/app/views/users/_profile.html.haml @@ -12,7 +12,7 @@ - unless user.linkedin.blank? %li %span.light LinkedIn: - %strong= user.linkedin + %strong= link_to user.linkedin, "http://www.linkedin.com/in/#{user.linkedin}" - unless user.twitter.blank? %li %span.light Twitter: diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml index 1d38f8e8ab8..6c7779be30e 100644 --- a/app/views/users/_projects.html.haml +++ b/app/views/users/_projects.html.haml @@ -1,6 +1,13 @@ -.panel.panel-default - .panel-heading Personal projects - %ul.well-list - - projects.each do |project| - %li - = link_to_project project +- if @contributed_projects.present? + .panel.panel-default + .panel-heading Projects contributed to + = render 'shared/projects_list', + projects: @contributed_projects.sort_by(&:star_count).reverse, + projects_limit: 5, stars: true, avatar: false + +- if @projects.present? + .panel.panel-default + .panel-heading Personal projects + = render 'shared/projects_list', + projects: @projects.sort_by(&:star_count).reverse, + projects_limit: 10, stars: true, avatar: false diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 13bdc5ed1e7..1d1c974da24 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -1,4 +1,4 @@ -%h4 Calendar +%h4 Commits calendar #cal-heatmap.calendar :javascript new calendar( diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index b05918b019e..abd6b229782 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,5 +1,7 @@ .row - .col-md-8 + = link_to '#aside', class: 'show-aside' do + %i.fa.fa-angle-left + %section.col-md-8 %h3.page-title = image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: '' = @user.name @@ -19,10 +21,11 @@ = render 'groups', groups: @groups %hr - .user-calendar - %h4.center.light - %i.fa.fa-spinner.fa-spin - %hr + .hidden-xs + .user-calendar + %h4.center.light + %i.fa.fa-spinner.fa-spin + %hr %h4 User Activity @@ -33,11 +36,9 @@ %i.fa.fa-rss = render @events - .col-md-4 + %aside.col-md-4 = render 'profile', user: @user - - if @projects.present? - = render 'projects', projects: @projects - + = render 'projects' :coffeescript $ -> diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb index e3f6f3a6aef..e59ca81defe 100644 --- a/app/workers/emails_on_push_worker.rb +++ b/app/workers/emails_on_push_worker.rb @@ -1,25 +1,41 @@ class EmailsOnPushWorker include Sidekiq::Worker - def perform(project_id, recipients, push_data) + def perform(project_id, recipients, push_data, send_from_committer_email = false, disable_diffs = false) project = Project.find(project_id) before_sha = push_data["before"] after_sha = push_data["after"] branch = push_data["ref"] author_id = push_data["user_id"] - if before_sha =~ /^000000/ || after_sha =~ /^000000/ + if Gitlab::Git.blank_ref?(before_sha) || Gitlab::Git.blank_ref?(after_sha) # skip if new branch was pushed or branch was removed return true end compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha) - # Do not send emails if git compare failed - return false unless compare && compare.commits.present? + return false if compare.same + + if compare.commits.empty? + compare = Gitlab::Git::Compare.new(project.repository.raw_repository, after_sha, before_sha) + + reverse_compare = true + + return false if compare.commits.empty? + end recipients.split(" ").each do |recipient| - Notify.repository_push_email(project_id, recipient, author_id, branch, compare).deliver + Notify.repository_push_email( + project_id, + recipient, + author_id, + branch, + compare, + reverse_compare, + send_from_committer_email, + disable_diffs + ).deliver end ensure compare = nil diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb new file mode 100644 index 00000000000..e1a99d9cad8 --- /dev/null +++ b/app/workers/irker_worker.rb @@ -0,0 +1,169 @@ +require 'json' +require 'socket' + +class IrkerWorker + include Sidekiq::Worker + + def perform(project_id, chans, colors, push_data, settings) + project = Project.find(project_id) + + # Get config parameters + return false unless init_perform settings, chans, colors + + repo_name = push_data['repository']['name'] + committer = push_data['user_name'] + branch = push_data['ref'].gsub(%r'refs/[^/]*/', '') + + if @colors + repo_name = "\x0304#{repo_name}\x0f" + branch = "\x0305#{branch}\x0f" + end + + # Firsts messages are for branch creation/deletion + send_branch_updates push_data, project, repo_name, committer, branch + + # Next messages are for commits + send_commits push_data, project, repo_name, committer, branch + + close_connection + true + end + + private + + def init_perform(set, chans, colors) + @colors = colors + @channels = chans + start_connection set['server_ip'], set['server_port'] + end + + def start_connection(irker_server, irker_port) + begin + @socket = TCPSocket.new irker_server, irker_port + rescue Errno::ECONNREFUSED => e + logger.fatal "Can't connect to Irker daemon: #{e}" + return false + end + true + end + + def sendtoirker(privmsg) + to_send = { to: @channels, privmsg: privmsg } + @socket.puts JSON.dump(to_send) + end + + def close_connection + @socket.close + end + + def send_branch_updates(push_data, project, repo_name, committer, branch) + if push_data['before'] == Gitlab::Git::BLANK_SHA + send_new_branch project, repo_name, committer, branch + elsif push_data['after'] == Gitlab::Git::BLANK_SHA + send_del_branch repo_name, committer, branch + end + end + + def send_new_branch(project, repo_name, committer, branch) + repo_path = project.path_with_namespace + newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches" + newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors + + privmsg = "[#{repo_name}] #{committer} has created a new branch " + privmsg += "#{branch}: #{newbranch}" + sendtoirker privmsg + end + + def send_del_branch(repo_name, committer, branch) + privmsg = "[#{repo_name}] #{committer} has deleted the branch #{branch}" + sendtoirker privmsg + end + + def send_commits(push_data, project, repo_name, committer, branch) + return if push_data['total_commits_count'] == 0 + + # Next message is for number of commit pushed, if any + if push_data['before'] == Gitlab::Git::BLANK_SHA + # Tweak on push_data["before"] in order to have a nice compare URL + push_data['before'] = before_on_new_branch push_data, project + end + + send_commits_count(push_data, project, repo_name, committer, branch) + + # One message per commit, limited by 3 messages (same limit as the + # github irc hook) + commits = push_data['commits'].first(3) + commits.each do |hook_attrs| + send_one_commit project, hook_attrs, repo_name, branch + end + end + + def before_on_new_branch(push_data, project) + commit = commit_from_id project, push_data['commits'][0]['id'] + parents = commit.parents + # Return old value if there's no new one + return push_data['before'] if parents.empty? + # Or return the first parent-commit + parents[0].id + end + + def send_commits_count(data, project, repo, committer, branch) + url = compare_url data, project.path_with_namespace + commits = colorize_commits data['total_commits_count'] + + new_commits = 'new commit' + new_commits += 's' if data['total_commits_count'] > 1 + + sendtoirker "[#{repo}] #{committer} pushed #{commits} #{new_commits} " \ + "to #{branch}: #{url}" + end + + def compare_url(data, repo_path) + sha1 = Commit::truncate_sha(data['before']) + sha2 = Commit::truncate_sha(data['after']) + compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" + compare_url += "/#{sha1}...#{sha2}" + colorize_url compare_url + end + + def send_one_commit(project, hook_attrs, repo_name, branch) + commit = commit_from_id project, hook_attrs['id'] + sha = colorize_sha Commit::truncate_sha(hook_attrs['id']) + author = hook_attrs['author']['name'] + files = colorize_nb_files(files_count commit) + title = commit.title + + sendtoirker "#{repo_name}/#{branch} #{sha} #{author} (#{files}): #{title}" + end + + def commit_from_id(project, id) + commit = Gitlab::Git::Commit.find(project.repository, id) + Commit.new(commit) + end + + def files_count(commit) + files = "#{commit.diffs.count} file" + files += 's' if commit.diffs.count > 1 + files + end + + def colorize_sha(sha) + sha = "\x0314#{sha}\x0f" if @colors + sha + end + + def colorize_nb_files(nb_files) + nb_files = "\x0312#{nb_files}\x0f" if @colors + nb_files + end + + def colorize_url(url) + url = "\x0302\x1f#{url}\x0f" if @colors + url + end + + def colorize_commits(commits) + commits = "\x02#{commits}\x0f" if @colors + commits + end +end diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 1406cba2db3..ecc6c8e53a3 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -33,7 +33,7 @@ class PostReceive return false end - if tag?(ref) + if Gitlab::Git.tag_ref?(ref) GitTagPushService.new.execute(project, @user, oldrev, newrev, ref) else GitPushService.new.execute(project, @user, oldrev, newrev, ref) @@ -44,10 +44,4 @@ class PostReceive def log(message) Gitlab::GitLogger.error("POST-RECEIVE: #{message}") end - - private - - def tag?(ref) - !!(/refs\/tags\/(.*)/.match(ref)) - end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 5f9970d3795..437640d2305 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -6,25 +6,27 @@ class RepositoryImportWorker def perform(project_id) project = Project.find(project_id) - result = gitlab_shell.send(:import_repository, + + import_result = gitlab_shell.send(:import_repository, project.path_with_namespace, project.import_url) + return project.import_fail unless import_result - result_of_data_import = if project.import_type == 'github' - Gitlab::GithubImport::Importer.new(project).execute - elsif project.import_type == 'gitlab' - Gitlab::GitlabImport::Importer.new(project).execute - else - true - end + data_import_result = if project.import_type == 'github' + Gitlab::GithubImport::Importer.new(project).execute + elsif project.import_type == 'gitlab' + Gitlab::GitlabImport::Importer.new(project).execute + elsif project.import_type == 'bitbucket' + Gitlab::BitbucketImport::Importer.new(project).execute + else + true + end + return project.import_fail unless data_import_result - if result && result_of_data_import - project.import_finish - project.save - project.satellite.create unless project.satellite.exists? - project.update_repository_size - else - project.import_fail - end + project.import_finish + project.save + project.satellite.create unless project.satellite.exists? + project.update_repository_size + Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' end end diff --git a/bin/rspec b/bin/rspec index 41e37089ac2..20060ebd79c 100755 --- a/bin/rspec +++ b/bin/rspec @@ -4,4 +4,4 @@ begin rescue LoadError end require 'bundler/setup' -load Gem.bin_path('rspec', 'rspec') +load Gem.bin_path('rspec-core', 'rspec') diff --git a/config/application.rb b/config/application.rb index bd4578848c5..fa399533e52 100644 --- a/config/application.rb +++ b/config/application.rb @@ -50,6 +50,8 @@ module Gitlab # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' + config.action_view.sanitized_allowed_protocols = %w(smb) + # Relative url support # Uncomment and customize the last line to run in a non-root path # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. diff --git a/config/environments/production.rb b/config/environments/production.rb index 78bf543402b..3316ece3873 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -11,8 +11,9 @@ Gitlab::Application.configure do # Disable Rails's static asset server (Apache or nginx will already do this) config.serve_static_assets = false - # Compress JavaScripts and CSS - config.assets.compress = true + # Compress JavaScripts and CSS. + config.assets.js_compressor = :uglifier + # config.assets.css_compressor = :sass # Don't fallback to assets pipeline if a precompiled asset is missed config.assets.compile = true @@ -74,7 +75,6 @@ Gitlab::Application.configure do config.action_mailer.raise_delivery_errors = true config.eager_load = true - config.assets.js_compressor = :uglifier config.allow_concurrency = false end diff --git a/config/environments/test.rb b/config/environments/test.rb index 25b082b98da..2d5e7addcd3 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -5,7 +5,7 @@ Gitlab::Application.configure do # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped # and recreated between test runs. Don't rely on the data there! - config.cache_classes = true + config.cache_classes = false # Configure static asset server for tests with Cache-Control for performance config.serve_static_assets = true diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 044b1f66b25..75d9e65aefe 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -43,6 +43,7 @@ production: &base # email_enabled: true # Email address used in the "From" field in mails sent by GitLab email_from: example@example.com + email_display_name: GitLab # Email server smtp settings are in config/initializers/smtp_settings.rb.sample @@ -207,17 +208,19 @@ production: &base # arguments, followed by optional 'args' which can be either a hash or an array. # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html providers: - # - { name: 'google_oauth2', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET', + # - { name: 'google_oauth2', app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET', # args: { access_type: 'offline', approval_prompt: '' } } - # - { name: 'twitter', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET'} - # - { name: 'github', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET', + # - { name: 'twitter', app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET'} + # - { name: 'github', app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET', # args: { scope: 'user:email' } } - # - { name: 'gitlab', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET', + # - { name: 'gitlab', app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET', # args: { scope: 'api' } } + # - { name: 'bitbucket', app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET'} diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index d7c1a8428ac..70af7a829c4 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -102,6 +102,7 @@ Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil? Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}" +Settings.gitlab['email_display_name'] ||= "GitLab" Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['user'] ||= 'git' Settings.gitlab['user_home'] ||= begin @@ -112,6 +113,7 @@ end Settings.gitlab['time_zone'] ||= nil Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil? Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil? +Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil? Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil? diff --git a/config/initializers/public_key.rb b/config/initializers/public_key.rb new file mode 100644 index 00000000000..75d74e3625d --- /dev/null +++ b/config/initializers/public_key.rb @@ -0,0 +1,2 @@ +path = File.expand_path("~/.ssh/id_rsa.pub") +Gitlab::BitbucketImport.public_key = File.read(path) if File.exist?(path) diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample index e00923e7e0c..f0fe2fdfa43 100644 --- a/config/initializers/smtp_settings.rb.sample +++ b/config/initializers/smtp_settings.rb.sample @@ -3,6 +3,9 @@ # 2. Edit settings inside this file # 3. Restart GitLab instance # +# For full list of options and their values see http://api.rubyonrails.org/classes/ActionMailer/Base.html +# + if Rails.env.production? Gitlab::Application.config.action_mailer.delivery_method = :smtp @@ -14,6 +17,6 @@ if Rails.env.production? domain: "gitlab.company.com", authentication: :login, enable_starttls_auto: true, - openssl_verify_mode: 'none' + openssl_verify_mode: 'peer' # See ActionMailer documentation for other possible options } end diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb new file mode 100644 index 00000000000..d9042c652bb --- /dev/null +++ b/config/initializers/static_files.rb @@ -0,0 +1,15 @@ +app = Rails.application + +if app.config.serve_static_assets + # The `ActionDispatch::Static` middleware intercepts requests for static files + # by checking if they exist in the `/public` directory. + # We're replacing it with our `Gitlab::Middleware::Static` that does the same, + # except ignoring `/uploads`, letting those go through to the GitLab Rails app. + + app.config.middleware.swap( + ActionDispatch::Static, + Gitlab::Middleware::Static, + app.paths["public"].first, + app.config.static_cache_control + ) +end diff --git a/config/initializers/timeout.rb b/config/initializers/timeout.rb new file mode 100644 index 00000000000..bc88595cf26 --- /dev/null +++ b/config/initializers/timeout.rb @@ -0,0 +1,8 @@ +# Slowpoke extends Rack::Timeout to gracefully kill Unicorn workers so they can clean up state. +Slowpoke.timeout = 60 + +# The `Rack::Timeout` middleware kills requests after 60 seconds (as set above). +# We're replacing it with our `Gitlab::Middleware::Timeout` that does the same, +# except ignoring Git-over-HTTP requests, letting those take as long as they need. + +Rails.application.config.middleware.swap(Rack::Timeout, Gitlab::Middleware::Timeout) diff --git a/config/routes.rb b/config/routes.rb index c8a8415ae77..889995e92a6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,9 +7,8 @@ Gitlab::Application.routes.draw do authorized_applications: 'oauth/authorized_applications', authorizations: 'oauth/authorizations' end - # + # Search - # get 'search' => 'search#show' get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete @@ -33,13 +32,11 @@ Gitlab::Application.routes.draw do receive_pack: Gitlab.config.gitlab_shell.receive_pack }), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post] - # # Help - # - get 'help' => 'help#index' get 'help/:category/:file' => 'help#show', as: :help_page get 'help/shortcuts' + get 'help/ui' => 'help#ui' # # Global snippets @@ -51,7 +48,7 @@ Gitlab::Application.routes.draw do end get '/s/:username' => 'snippets#user_index', as: :user_snippets, constraints: { username: /.*/ } - + # # Import # @@ -67,9 +64,40 @@ Gitlab::Application.routes.draw do get :callback get :jobs end + + resource :bitbucket, only: [:create, :new], controller: :bitbucket do + get :status + get :callback + get :jobs + end + + resource :gitorious, only: [:create, :new], controller: :gitorious do + get :status + get :callback + get :jobs + end end - - + + # + # Uploads + # + + scope path: :uploads do + # Note attachments and User/Group/Project avatars + get ":model/:mounted_as/:id/:filename", + to: "uploads#show", + constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /.+/ } + + # Project markdown uploads + get ":namespace_id/:project_id/:secret/:filename", + to: "projects/uploads#show", + constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: /.+/ } + end + + # Redirect old note attachments path to new uploads path. + get "files/note/:id/:filename", + to: redirect("uploads/note/attachment/%{id}/%{filename}"), + constraints: { filename: /.+/ } # # Explore area @@ -91,11 +119,6 @@ Gitlab::Application.routes.draw do get 'public/projects' => 'explore/projects#index' # - # Attachments serving - # - get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /.+/ } - - # # Admin Area # namespace :admin do @@ -125,13 +148,24 @@ Gitlab::Application.routes.draw do resource :logs, only: [:show] resource :background_jobs, controller: 'background_jobs', only: [:show] - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do - member do - put :transfer + resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do + root to: 'projects#index', as: :projects + + resources(:projects, + path: '/', + constraints: { id: /[a-zA-Z.0-9_\-]+/ }, + only: [:index, :show]) do + root to: 'projects#show' + + member do + put :transfer + end end end - resource :application_settings, only: [:show, :update] + resource :application_settings, only: [:show, :update] do + resources :services + end root to: 'dashboard#index' end @@ -159,11 +193,6 @@ Gitlab::Application.routes.draw do end resources :keys resources :emails, only: [:index, :create, :destroy] - resources :groups, only: [:index] do - member do - delete :leave - end - end resource :avatar, only: [:destroy] end end @@ -179,10 +208,25 @@ Gitlab::Application.routes.draw do # resource :dashboard, controller: 'dashboard', only: [:show] do member do - get :projects get :issues get :merge_requests end + + scope module: :dashboard do + resources :milestones, only: [:index, :show] + + resources :groups, only: [:index] do + member do + delete :leave + end + end + + resources :projects, only: [] do + collection do + get :starred + end + end + end end # @@ -199,7 +243,7 @@ Gitlab::Application.routes.draw do scope module: :groups do resources :group_members, only: [:create, :update, :destroy] resource :avatar, only: [:destroy] - resources :milestones + resources :milestones, only: [:index, :show, :update] end end @@ -210,167 +254,218 @@ Gitlab::Application.routes.draw do devise_scope :user do get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error end + + root to: "dashboard#show" + # # Project Area # - resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: '/' do - member do - put :transfer - post :archive - post :unarchive - post :upload_image - post :toggle_star - post :markdown_preview - get :autocomplete_sources - end - - scope module: :projects do - # Blob routes: - get '/new/:id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob' - post '/create/:id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob' - get '/edit/:id', to: 'blob#edit', constraints: { id: /.+/ }, as: 'edit_blob' - put '/update/:id', to: 'blob#update', constraints: { id: /.+/ }, as: 'update_blob' - post '/preview/:id', to: 'blob#preview', constraints: { id: /.+/ }, as: 'preview_blob' - - resources :blob, only: [:show, :destroy], constraints: { id: /.+/, format: false } do - get :diff, on: :member + resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do + resources(:projects, constraints: { id: /[a-zA-Z.0-9_\-]+/ }, except: + [:new, :create, :index], path: "/") do + member do + put :transfer + post :archive + post :unarchive + post :toggle_star + post :markdown_preview + get :autocomplete_sources end - resources :raw, only: [:show], constraints: { id: /.+/ } - resources :tree, only: [:show], constraints: { id: /.+/, format: /(html|js)/ } - resource :avatar, only: [:show, :destroy] - - resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do - get :branches, on: :member - end + scope module: :projects do + # Blob routes: + get '/new/*id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob' + post '/create/*id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob' + get '/edit/*id', to: 'blob#edit', constraints: { id: /.+/ }, as: 'edit_blob' + put '/update/*id', to: 'blob#update', constraints: { id: /.+/ }, as: 'update_blob' + post '/preview/*id', to: 'blob#preview', constraints: { id: /.+/ }, as: 'preview_blob' + + scope do + get( + '/blob/*id/diff', + to: 'blob#diff', + constraints: { id: /.+/, format: false }, + as: :blob_diff + ) + get( + '/blob/*id', + to: 'blob#show', + constraints: { id: /.+/, format: false }, + as: :blob + ) + delete( + '/blob/*id', + to: 'blob#destroy', + constraints: { id: /.+/, format: false } + ) + end - resources :commits, only: [:show], constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } - resources :compare, only: [:index, :create] - resources :blame, only: [:show], constraints: { id: /.+/ } - resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } - resources :graphs, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } do - member do - get :commits + scope do + get( + '/raw/*id', + to: 'raw#show', + constraints: { id: /.+/, format: /(html|js)/ }, + as: :raw + ) end - end - get '/compare/:from...:to' => 'compare#show', :as => 'compare', - :constraints => { from: /.+/, to: /.+/ } + scope do + get( + '/tree/*id', + to: 'tree#show', + constraints: { id: /.+/, format: /(html|js)/ }, + as: :tree + ) + end + resource :avatar, only: [:show, :destroy] - resources :snippets, constraints: { id: /\d+/ } do - member do - get 'raw' + resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do + get :branches, on: :member end - end - resources :wikis, only: [:show, :edit, :destroy, :create], constraints: { id: /[a-zA-Z.0-9_\-\/]+/ } do - collection do - get :pages - put ':id' => 'wikis#update' - get :git_access + resources :commits, only: [:show], constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } + resources :compare, only: [:index, :create] + + scope do + get( + '/blame/*id', + to: 'blame#show', + constraints: { id: /.+/, format: /(html|js)/ }, + as: :blame + ) end - member do - get 'history' + resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } + resources :graphs, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } do + member do + get :commits + end end - end - resource :fork, only: [:new, :create] - resource :import, only: [:new, :create, :show] + get '/compare/:from...:to' => 'compare#show', :as => 'compare', + :constraints => { from: /.+/, to: /.+/ } - resource :repository, only: [:show, :create] do - member do - get 'archive', constraints: { format: Gitlab::Regex.archive_formats_regex } + resources :snippets, constraints: { id: /\d+/ } do + member do + get 'raw' + end end - end - resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do - member do - get :test - end - end + resources :wikis, only: [:show, :edit, :destroy, :create], constraints: { id: /[a-zA-Z.0-9_\-\/]+/ } do + collection do + get :pages + put ':id' => 'wikis#update' + get :git_access + end - resources :deploy_keys, constraints: { id: /\d+/ } do - member do - put :enable - put :disable + member do + get 'history' + end end - end - resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } - resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } - resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } + resource :repository, only: [:show, :create] do + member do + get 'archive', constraints: { format: Gitlab::Regex.archive_formats_regex } + end + end - resources :refs, only: [] do - collection do - get 'switch' + resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do + member do + get :test + end end - member do - # tree viewer logs - get 'logs_tree', constraints: { id: Gitlab::Regex.git_reference_regex } - get 'logs_tree/:path' => 'refs#logs_tree', as: :logs_file, constraints: { - id: Gitlab::Regex.git_reference_regex, - path: /.*/ - } + resources :deploy_keys, constraints: { id: /\d+/ } do + member do + put :enable + put :disable + end end - end - resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do - member do - get :diffs - post :automerge - get :automerge_check - get :ci_status + resource :fork, only: [:new, :create] + resource :import, only: [:new, :create, :show] + + resources :refs, only: [] do + collection do + get 'switch' + end + + member do + # tree viewer logs + get 'logs_tree', constraints: { id: Gitlab::Regex.git_reference_regex } + get 'logs_tree/:path' => 'refs#logs_tree', as: :logs_file, constraints: { + id: Gitlab::Regex.git_reference_regex, + path: /.*/ + } + end end - collection do - get :branch_from - get :branch_to - get :update_branches + resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do + member do + get :diffs + post :automerge + get :automerge_check + get :ci_status + end + + collection do + get :branch_from + get :branch_to + get :update_branches + end end - end - resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do - member do - get :test + resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } + resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } + resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } + + resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do + member do + get :test + end end - end - resources :team, controller: 'team_members', only: [:index] - resources :milestones, except: [:destroy], constraints: { id: /\d+/ } do - member do - put :sort_issues - put :sort_merge_requests + resources :team, controller: 'team_members', only: [:index] + resources :milestones, except: [:destroy], constraints: { id: /\d+/ } do + member do + put :sort_issues + put :sort_merge_requests + end end - end - resources :labels, constraints: { id: /\d+/ } do - collection do - post :generate + resources :labels, constraints: { id: /\d+/ } do + collection do + post :generate + end end - end - resources :issues, constraints: { id: /\d+/ }, except: [:destroy] do - collection do - post :bulk_update + resources :issues, constraints: { id: /\d+/ }, except: [:destroy] do + collection do + post :bulk_update + end end - end - resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do - collection do - delete :leave + resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do + collection do + delete :leave - # Used for import team - # from another project - get :import - post :apply_import + # Used for import team + # from another project + get :import + post :apply_import + end end - end - resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do - member do - delete :delete_attachment + resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do + member do + delete :delete_attachment + end + end + + resources :uploads, only: [:create] do + collection do + get ":secret/:filename", action: :show, as: :show, constraints: { filename: /.+/ } + end end end @@ -378,6 +473,4 @@ Gitlab::Application.routes.draw do end get ':id' => 'namespaces#show', constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } - - root to: 'dashboard#show' end diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example index d8b4f5c7c32..29253b71f49 100644 --- a/config/unicorn.rb.example +++ b/config/unicorn.rb.example @@ -35,22 +35,10 @@ working_directory "/home/git/gitlab" # available in 0.94.0+ listen "/home/git/gitlab/tmp/sockets/gitlab.socket", :backlog => 1024 listen "127.0.0.1:8080", :tcp_nopush => true -# nuke workers after 30 seconds instead of 60 seconds (the default) -# -# NOTICE: git push over http depends on this value. -# If you want be able to push huge amount of data to git repository over http -# you will have to increase this value too. -# -# Example of output if you try to push 1GB repo to GitLab over http. -# -> git push http://gitlab.... master -# -# error: RPC failed; result=18, HTTP code = 200 -# fatal: The remote end hung up unexpectedly -# fatal: The remote end hung up unexpectedly -# -# For more information see http://stackoverflow.com/a/21682112/752049 -# -timeout 60 +# Kill workers after 1 hour. +# A shorter timeout of 60 seconds is enforced by rack-timeout for web requests. +# Git-over-HTTP only has the below timeout since large pulls/pushes can take a long time. +timeout 60 * 60 # feel free to point this anywhere accessible on the filesystem pid "/home/git/gitlab/tmp/pids/unicorn.pid" diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb index 1b2dec31323..bba2fc4b186 100644 --- a/db/fixtures/development/01_admin.rb +++ b/db/fixtures/development/01_admin.rb @@ -3,6 +3,7 @@ Gitlab::Seeder.quiet do s.id = 1 s.name = 'Administrator' s.email = 'admin@example.com' + s.notification_email = 'admin@example.com' s.username = 'root' s.password = '5iveL!fe' s.admin = true diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb index b697f58d4ef..24952a1f661 100644 --- a/db/fixtures/development/05_users.rb +++ b/db/fixtures/development/05_users.rb @@ -1,30 +1,31 @@ Gitlab::Seeder.quiet do (2..20).each do |i| begin - User.seed(:id, [{ - id: i, + User.create!( username: Faker::Internet.user_name, name: Faker::Name.name, email: Faker::Internet.email, - confirmed_at: DateTime.now - }]) + confirmed_at: DateTime.now, + password: '12345678' + ) + print '.' - rescue ActiveRecord::RecordNotSaved + rescue ActiveRecord::RecordInvalid print 'F' end end (1..5).each do |i| begin - User.seed do |s| - s.username = "user#{i}" - s.name = "User #{i}" - s.email = "user#{i}@example.com" - s.confirmed_at = DateTime.now - s.password = '12345678' - end + User.create!( + username: "user#{i}", + name: "User #{i}", + email: "user#{i}@example.com", + confirmed_at: DateTime.now, + password: '12345678' + ) print '.' - rescue ActiveRecord::RecordNotSaved + rescue ActiveRecord::RecordInvalid print 'F' end end diff --git a/db/migrate/20140907220153_serialize_service_properties.rb b/db/migrate/20140907220153_serialize_service_properties.rb index bd75ab1eacb..d45a10465be 100644 --- a/db/migrate/20140907220153_serialize_service_properties.rb +++ b/db/migrate/20140907220153_serialize_service_properties.rb @@ -1,6 +1,9 @@ class SerializeServiceProperties < ActiveRecord::Migration def change - add_column :services, :properties, :text + unless column_exists?(:services, :properties) + add_column :services, :properties, :text + end + Service.reset_column_information associations = @@ -19,18 +22,21 @@ class SerializeServiceProperties < ActiveRecord::Migration :api_version, :jira_issue_transition_id], } - Service.all.each do |service| + Service.find_each(batch_size: 500).each do |service| associations[service.type.to_sym].each do |attribute| service.send("#{attribute}=", service.attributes[attribute.to_s]) end - service.save + + service.save(validate: false) end - remove_column :services, :project_url, :string - remove_column :services, :subdomain, :string - remove_column :services, :room, :string - remove_column :services, :recipients, :text - remove_column :services, :api_key, :string - remove_column :services, :token, :string + if column_exists?(:services, :project_url) + remove_column :services, :project_url, :string + remove_column :services, :subdomain, :string + remove_column :services, :room, :string + remove_column :services, :recipients, :text + remove_column :services, :api_key, :string + remove_column :services, :token, :string + end end end diff --git a/db/migrate/20141006143943_move_slack_service_to_webhook.rb b/db/migrate/20141006143943_move_slack_service_to_webhook.rb index a8e07033a5d..5836cd6b8db 100644 --- a/db/migrate/20141006143943_move_slack_service_to_webhook.rb +++ b/db/migrate/20141006143943_move_slack_service_to_webhook.rb @@ -10,7 +10,7 @@ class MoveSlackServiceToWebhook < ActiveRecord::Migration slack_service.properties.delete('subdomain') # Room is configured on the Slack side slack_service.properties.delete('room') - slack_service.save + slack_service.save(validate: false) end end end diff --git a/db/migrate/20150211172122_add_template_to_service.rb b/db/migrate/20150211172122_add_template_to_service.rb new file mode 100644 index 00000000000..b1bfbc45ee9 --- /dev/null +++ b/db/migrate/20150211172122_add_template_to_service.rb @@ -0,0 +1,5 @@ +class AddTemplateToService < ActiveRecord::Migration + def change + add_column :services, :template, :boolean, default: false + end +end diff --git a/db/migrate/20150211174341_allow_null_in_services_project_id.rb b/db/migrate/20150211174341_allow_null_in_services_project_id.rb new file mode 100644 index 00000000000..68f02812791 --- /dev/null +++ b/db/migrate/20150211174341_allow_null_in_services_project_id.rb @@ -0,0 +1,5 @@ +class AllowNullInServicesProjectId < ActiveRecord::Migration + def change + change_column :services, :project_id, :integer, null: true + end +end diff --git a/db/migrate/20150213104043_add_twitter_sharing_enabled_to_application_settings.rb b/db/migrate/20150213104043_add_twitter_sharing_enabled_to_application_settings.rb new file mode 100644 index 00000000000..a0439172391 --- /dev/null +++ b/db/migrate/20150213104043_add_twitter_sharing_enabled_to_application_settings.rb @@ -0,0 +1,5 @@ +class AddTwitterSharingEnabledToApplicationSettings < ActiveRecord::Migration + def change + add_column :application_settings, :twitter_sharing_enabled, :boolean, default: true + end +end diff --git a/db/migrate/20150213114800_add_hide_no_password_to_user.rb b/db/migrate/20150213114800_add_hide_no_password_to_user.rb new file mode 100644 index 00000000000..685f0844276 --- /dev/null +++ b/db/migrate/20150213114800_add_hide_no_password_to_user.rb @@ -0,0 +1,5 @@ +class AddHideNoPasswordToUser < ActiveRecord::Migration + def change + add_column :users, :hide_no_password, :boolean, default: false + end +end diff --git a/db/migrate/20150213121042_add_password_automatically_set_to_user.rb b/db/migrate/20150213121042_add_password_automatically_set_to_user.rb new file mode 100644 index 00000000000..c3c7c1ffc77 --- /dev/null +++ b/db/migrate/20150213121042_add_password_automatically_set_to_user.rb @@ -0,0 +1,5 @@ +class AddPasswordAutomaticallySetToUser < ActiveRecord::Migration + def change + add_column :users, :password_automatically_set, :boolean, default: false + end +end diff --git a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb new file mode 100644 index 00000000000..23ac1b399ec --- /dev/null +++ b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb @@ -0,0 +1,6 @@ +class AddBitbucketAccessTokenAndSecretToUser < ActiveRecord::Migration + def change + add_column :users, :bitbucket_access_token, :string + add_column :users, :bitbucket_access_token_secret, :string + end +end diff --git a/db/migrate/20150219004514_add_events_to_services.rb b/db/migrate/20150219004514_add_events_to_services.rb new file mode 100644 index 00000000000..cf73a0174f4 --- /dev/null +++ b/db/migrate/20150219004514_add_events_to_services.rb @@ -0,0 +1,8 @@ +class AddEventsToServices < ActiveRecord::Migration + def change + add_column :services, :push_events, :boolean, :default => true + add_column :services, :issues_events, :boolean, :default => true + add_column :services, :merge_requests_events, :boolean, :default => true + add_column :services, :tag_push_events, :boolean, :default => true + end +end diff --git a/db/migrate/20150223022001_set_missing_last_activity_at.rb b/db/migrate/20150223022001_set_missing_last_activity_at.rb new file mode 100644 index 00000000000..3f6d4d83474 --- /dev/null +++ b/db/migrate/20150223022001_set_missing_last_activity_at.rb @@ -0,0 +1,8 @@ +class SetMissingLastActivityAt < ActiveRecord::Migration + def up + execute "UPDATE projects SET last_activity_at = updated_at WHERE last_activity_at IS NULL" + end + + def down + end +end diff --git a/db/migrate/20150225065047_add_note_events_to_services.rb b/db/migrate/20150225065047_add_note_events_to_services.rb new file mode 100644 index 00000000000..d54ba9e482f --- /dev/null +++ b/db/migrate/20150225065047_add_note_events_to_services.rb @@ -0,0 +1,5 @@ +class AddNoteEventsToServices < ActiveRecord::Migration + def change + add_column :services, :note_events, :boolean, default: true, null: false + end +end diff --git a/db/migrate/20150306023106_fix_namespace_duplication.rb b/db/migrate/20150306023106_fix_namespace_duplication.rb new file mode 100644 index 00000000000..334e5574559 --- /dev/null +++ b/db/migrate/20150306023106_fix_namespace_duplication.rb @@ -0,0 +1,21 @@ +class FixNamespaceDuplication < ActiveRecord::Migration + def up + #fixes path duplication + select_all('SELECT MAX(id) max, COUNT(id) cnt, path FROM namespaces GROUP BY path HAVING COUNT(id) > 1').each do |nms| + bad_nms_ids = select_all("SELECT id FROM namespaces WHERE path = '#{nms['path']}' AND id <> #{nms['max']}").map{|x| x["id"]} + execute("UPDATE projects SET namespace_id = #{nms["max"]} WHERE namespace_id IN(#{bad_nms_ids.join(', ')})") + execute("DELETE FROM namespaces WHERE id IN(#{bad_nms_ids.join(', ')})") + end + + #fixes name duplication + select_all('SELECT MAX(id) max, COUNT(id) cnt, name FROM namespaces GROUP BY name HAVING COUNT(id) > 1').each do |nms| + bad_nms_ids = select_all("SELECT id FROM namespaces WHERE name = '#{nms['name']}' AND id <> #{nms['max']}").map{|x| x["id"]} + execute("UPDATE projects SET namespace_id = #{nms["max"]} WHERE namespace_id IN(#{bad_nms_ids.join(', ')})") + execute("DELETE FROM namespaces WHERE id IN(#{bad_nms_ids.join(', ')})") + end + end + + def down + # not implemented + end +end diff --git a/db/migrate/20150306023112_add_unique_index_to_namespace.rb b/db/migrate/20150306023112_add_unique_index_to_namespace.rb new file mode 100644 index 00000000000..6472138e3ef --- /dev/null +++ b/db/migrate/20150306023112_add_unique_index_to_namespace.rb @@ -0,0 +1,9 @@ +class AddUniqueIndexToNamespace < ActiveRecord::Migration + def change + remove_index :namespaces, column: :name if index_exists?(:namespaces, :name) + remove_index :namespaces, column: :path if index_exists?(:namespaces, :path) + + add_index :namespaces, :name, unique: true + add_index :namespaces, :path, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 8b6142a80a0..3afbc082b70 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150209222013) do +ActiveRecord::Schema.define(version: 20150306023112) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -26,6 +26,7 @@ ActiveRecord::Schema.define(version: 20150209222013) do t.datetime "updated_at" t.string "home_page_url" t.integer "default_branch_protection", default: 2 + t.boolean "twitter_sharing_enabled", default: true end create_table "broadcast_messages", force: true do |t| @@ -241,9 +242,9 @@ ActiveRecord::Schema.define(version: 20150209222013) do end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree - add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree + add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree - add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree + add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree create_table "notes", force: true do |t| @@ -360,11 +361,17 @@ ActiveRecord::Schema.define(version: 20150209222013) do create_table "services", force: true do |t| t.string "type" t.string "title" - t.integer "project_id", null: false + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.boolean "active", default: false, null: false t.text "properties" + t.boolean "template", default: false + t.boolean "push_events", default: true + t.boolean "issues_events", default: true + t.boolean "merge_requests_events", default: true + t.boolean "tag_push_events", default: true + t.boolean "note_events", default: true, null: false end add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree @@ -408,12 +415,12 @@ ActiveRecord::Schema.define(version: 20150209222013) do end create_table "users", force: true do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" @@ -421,35 +428,39 @@ ActiveRecord::Schema.define(version: 20150209222013) do t.datetime "created_at" t.datetime "updated_at" t.string "name" - t.boolean "admin", default: false, null: false - t.integer "projects_limit", default: 10 - t.string "skype", default: "", null: false - t.string "linkedin", default: "", null: false - t.string "twitter", default: "", null: false + t.boolean "admin", default: false, null: false + t.integer "projects_limit", default: 10 + t.string "skype", default: "", null: false + t.string "linkedin", default: "", null: false + t.string "twitter", default: "", null: false t.string "authentication_token" - t.integer "theme_id", default: 1, null: false + t.integer "theme_id", default: 1, null: false t.string "bio" - t.integer "failed_attempts", default: 0 + t.integer "failed_attempts", default: 0 t.datetime "locked_at" t.string "username" - t.boolean "can_create_group", default: true, null: false - t.boolean "can_create_team", default: true, null: false + t.boolean "can_create_group", default: true, null: false + t.boolean "can_create_team", default: true, null: false t.string "state" - t.integer "color_scheme_id", default: 1, null: false - t.integer "notification_level", default: 1, null: false + t.integer "color_scheme_id", default: 1, null: false + t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" + t.datetime "last_credential_check_at" t.string "avatar" t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "unconfirmed_email" - t.boolean "hide_no_ssh_key", default: false - t.string "website_url", default: "", null: false - t.datetime "last_credential_check_at" + t.boolean "hide_no_ssh_key", default: false + t.string "website_url", default: "", null: false t.string "github_access_token" t.string "gitlab_access_token" t.string "notification_email" + t.boolean "hide_no_password", default: false + t.boolean "password_automatically_set", default: false + t.string "bitbucket_access_token" + t.string "bitbucket_access_token_secret" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/doc/README.md b/doc/README.md index 8c6d13e8506..4e00dceac2b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -10,10 +10,11 @@ - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. - [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. +- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab. ## Administrator documentation -- [Install](install/README.md) Requirements, directory structures and manual installation. +- [Install](install/README.md) Requirements, directory structures and installation from source. - [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter. - [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects. - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough. @@ -24,6 +25,7 @@ - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Operations](operations/README.md) Keeping GitLab up and running +- [Log system](logs/logs.md) Log system ## Contributor documentation diff --git a/doc/api/README.md b/doc/api/README.md index 8cbba8598d5..dec530d0b81 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -22,6 +22,7 @@ ## Clients Find API Clients for GitLab [on our website](https://about.gitlab.com/applications/#api-clients). +You can use [GitLab as an OAuth2 client](oauth2.md) to make API calls. ## Introduction @@ -67,7 +68,7 @@ curl https://localhost:3000/api/v3/user?access_token=OAUTH-TOKEN curl -H "Authorization: Bearer OAUTH-TOKEN" https://localhost:3000/api/v3/user ``` -Read more about [OAuth2 in GitLab](oauth2.md). +Read more about [GitLab as an OAuth2 client](oauth2.md). ## Status codes diff --git a/doc/api/branches.md b/doc/api/branches.md index 319f0b47386..6a9c10c8520 100644 --- a/doc/api/branches.md +++ b/doc/api/branches.md @@ -15,27 +15,20 @@ Parameters: ```json [ { - "name": "master", "commit": { + "author_email": "john@example.com", + "author_name": "John Smith", + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00", + "committer_email": "john@example.com", + "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" + "parent_ids": [ + "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + ] }, + "name": "master", "protected": true } ] @@ -56,27 +49,20 @@ Parameters: ```json { - "name": "master", "commit": { + "author_email": "john@example.com", + "author_name": "John Smith", + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00", + "committer_email": "john@example.com", + "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" + "parent_ids": [ + "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + ] }, + "name": "master", "protected": true } ``` @@ -97,27 +83,20 @@ Parameters: ```json { - "name": "master", "commit": { + "author_email": "john@example.com", + "author_name": "John Smith", + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00", + "committer_email": "john@example.com", + "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" + "parent_ids": [ + "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + ] }, + "name": "master", "protected": true } ``` @@ -138,27 +117,20 @@ Parameters: ```json { - "name": "master", "commit": { + "author_email": "john@example.com", + "author_name": "John Smith", + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00", + "committer_email": "john@example.com", + "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" + "parent_ids": [ + "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + ] }, + "name": "master", "protected": false } ``` @@ -177,21 +149,20 @@ Parameters: ```json { - "name": "my-new-branch", "commit": { - "id": "8848c0e90327a0b70f1865b843fb2fbfb9345e57", - "message": "Merge pull request #54 from brightbox/use_fog_brightbox_module\n\nUpdate to use fog-brightbox module", - "parent_ids": [ - "fff449e0bf453576f16c91d6544f00a2664009d8", - "f93a93626fec20fd659f4ed3ab2e64019b6169ae" - ], - "authored_date": "2014-02-20T19:54:55+02:00", - "author_name": "john smith", "author_email": "john@example.com", - "committed_date": "2014-02-20T19:54:55+02:00", - "committer_name": "john smith", - "committer_email": "john@example.com" + "author_name": "John Smith", + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00", + "committer_email": "john@example.com", + "committer_name": "John Smith", + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "message": "add projects API", + "parent_ids": [ + "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + ] }, + "name": "master", "protected": false } ``` diff --git a/doc/api/groups.md b/doc/api/groups.md index 9f01b550641..b5a4b05ccaf 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -14,7 +14,6 @@ GET /groups "id": 1, "name": "Foobar Group", "path": "foo-bar", - "owner_id": 18, "description": "An interesting group" } ] @@ -87,7 +86,6 @@ GET /groups?search=foobar "id": 1, "name": "Foobar Group", "path": "foo-bar", - "owner_id": 18, "description": "An interesting group" } ] @@ -152,6 +150,20 @@ Parameters: - `user_id` (required) - The ID of a user to add - `access_level` (required) - Project access level +### Edit group team member + +Updates a group team member to a specified access level. + +``` +PUT /groups/:id/members/:user_id +``` + +Parameters: + +- `id` (required) - The ID of a group +- `user_id` (required) - The ID of a group member +- `access_level` (required) - Project access level + ### Remove user team member Removes user from user team. diff --git a/doc/api/issues.md b/doc/api/issues.md index 5a2f6a4c229..a7dd8b74c35 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -208,7 +208,7 @@ If an error occurs, an error number and a message explaining the reason is retur ## Delete existing issue (**Deprecated**) -The function is deprecated and returns a `405 Method Not Allowed` error if called. An issue gets now closed and is done by calling `PUT /projects/:id/issues/:issue_id` with parameter `closed` set to 1. +The function is deprecated and returns a `405 Method Not Allowed` error if called. An issue gets now closed and is done by calling `PUT /projects/:id/issues/:issue_id` with parameter `state_event` set to `close`. ``` DELETE /projects/:id/issues/:issue_id diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index 7bb391054ce..d416a826f79 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -1,14 +1,17 @@ -# OAuth2 authentication +# GitLab as an OAuth2 client -OAuth2 is a protocol that enables us to get access to private details of user's account without getting its password. +This document is about using other OAuth authentication service providers to sign into GitLab. +If you want GitLab to be an OAuth authentication service provider to sign into other services please see the [Oauth2 provider documentation](../integration/oauth_provider.md). -Before using the OAuth2 you should create an application in user's account. Each application getting unique App ID and App Secret parameters. You should not share them. +OAuth2 is a protocol that enables us to authenticate a user without requiring them to give their password. + +Before using the OAuth2 you should create an application in user's account. Each application gets a unique App ID and App Secret parameters. You should not share these. This functionality is based on [doorkeeper gem](https://github.com/doorkeeper-gem/doorkeeper) ## Web Application Flow -This flow is using for authentication from third-party web sites and probably is most used. +This flow is using for authentication from third-party web sites and is probably used the most. It basically consists of an exchange of an authorization token for an access token. For more detailed info, check out the [RFC spec here](http://tools.ietf.org/html/rfc6749#section-4.1) This flow consists from 3 steps. diff --git a/doc/api/projects.md b/doc/api/projects.md index 454f6fa2e91..7fe244477db 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1,5 +1,23 @@ # Projects + +### Project visibility level + +Project in GitLab has be either private, internal or public. +You can determine it by `visibility_level` field in project. + +Constants for project visibility levels are next: + +* Private. `visibility_level` is `0`. + Project access must be granted explicitly for each user. + +* Internal. `visibility_level` is `10`. + The project can be cloned by any logged in user. + +* Public. `visibility_level` is `20`. + The project can be cloned without any authentication. + + ## List projects Get a list of projects accessible by the authenticated user. @@ -50,7 +68,8 @@ Parameters: "path": "diaspora", "updated_at": "2013-09-30T13: 46: 02Z" }, - "archived": false + "archived": false, + "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png" }, { "id": 6, @@ -85,7 +104,8 @@ Parameters: "path": "brightbox", "updated_at": "2013-09-30T13:46:02Z" }, - "archived": false + "archived": false, + "avatar_url": null } ] ``` @@ -177,7 +197,8 @@ Parameters: "notification_level": 3 } }, - "archived": false + "archived": false, + "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png" } ``` diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 8acf85d21c8..33167453802 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -15,24 +15,21 @@ Parameters: ```json [ { - "name": "v1.0.0", "commit": { + "author_name": "John Smith", + "author_email": "john@example.com", + "authored_date": "2012-05-28T04:42:42-07:00", + "committed_date": "2012-05-28T04:42:42-07:00", + "committer_name": "Jack Smith", + "committer_email": "jack@example.com", "id": "2695effb5807a22ff3d138d593fd856244e155e7", - "parents": [], - "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", "message": "Initial commit", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "Jack Smith", - "email": "jack@example.com" - }, - "authored_date": "2012-05-28T04:42:42-07:00", - "committed_date": "2012-05-28T04:42:42-07:00" + "parents_ids": [ + "2a4b78934375d7f53875269ffd4f45fd83a84ebe" + ] }, - "protected": null + "name": "v1.0.0", + "message": null } ] ``` @@ -53,23 +50,23 @@ Parameters: - `message` (optional) - Creates annotated tag. ```json -[ - { - "name": "v1.0.0", - "message": "Release 1.0.0", - "commit": { - "id": "2695effb5807a22ff3d138d593fd856244e155e7", - "parents": [], - "message": "Initial commit", - "authored_date": "2012-05-28T04:42:42-07:00", - "author_name": "John Smith", - "author email": "john@example.com", - "committer_name": "Jack Smith", - "committed_date": "2012-05-28T04:42:42-07:00", - "committer_email": "jack@example.com" - }, - } -] +{ + "commit": { + "author_name": "John Smith", + "author_email": "john@example.com", + "authored_date": "2012-05-28T04:42:42-07:00", + "committed_date": "2012-05-28T04:42:42-07:00", + "committer_name": "Jack Smith", + "committer_email": "jack@example.com", + "id": "2695effb5807a22ff3d138d593fd856244e155e7", + "message": "Initial commit", + "parents_ids": [ + "2a4b78934375d7f53875269ffd4f45fd83a84ebe" + ] + }, + "name": "v1.0.0", + "message": null +} ``` The message will be `nil` when creating a lightweight tag otherwise it will contain the annotation. diff --git a/doc/api/users.md b/doc/api/users.md index 71fa62bdd65..a8b7685b503 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -170,6 +170,7 @@ Parameters: - `bio` (optional) - User's biography - `admin` (optional) - User is admin - true or false (default) - `can_create_group` (optional) - User can create groups - true or false +- `confirm` (optional) - Require confirmation - true (default) or false ## User modification diff --git a/doc/development/README.md b/doc/development/README.md index c31e5d7ae97..d5d264be19d 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -5,3 +5,4 @@ - [Rake tasks](rake_tasks.md) for development - [CI setup](ci_setup.md) for testing GitLab - [Sidekiq debugging](sidekiq_debugging.md) +- [UI guide](ui_guide.md) for building GitLab with existing css styles and elements diff --git a/doc/development/ci_setup.md b/doc/development/ci_setup.md index f417667754e..f9b48868182 100644 --- a/doc/development/ci_setup.md +++ b/doc/development/ci_setup.md @@ -37,7 +37,10 @@ bundle install --deployment --path vendor/bundle (Setup) cp config/gitlab.yml.example config/gitlab.yml (Setup) bundle exec rake db:create (Setup) bundle exec rake spinach (Thread #1) -bundle exec rake spec (Thread #2) +bundle exec rake spec (thread #2) +bundle exec rake rubocop (thread #3) +bundle exec rake brakeman (thread #4) +bundle exec rake jasmine:ci (thread #5) ``` Use rubygems mirror. diff --git a/doc/development/omnibus.md b/doc/development/omnibus.md new file mode 100644 index 00000000000..0ba354d28a2 --- /dev/null +++ b/doc/development/omnibus.md @@ -0,0 +1,32 @@ +# What you should know about omnibus packages + +Most users install GitLab using our omnibus packages. As a developer it can be +good to know how the omnibus packages differ from what you have on your laptop +when you are coding. + +## Files are owned by root by default + +All the files in the Rails tree (`app/`, `config/` etc.) are owned by 'root' in +omnibus installations. This makes the installation simpler and it provides +extra security. The omnibus reconfigure script contains commands that give +write access to the 'git' user only where needed. + +For example, the 'git' user is allowed to write in the `log/` directory, in +`public/uploads`, and they are allowed to rewrite the `db/schema.rb` file. + +In other cases, the reconfigure script tricks GitLab into not trying to write a +file. For instance, GitLab will generate a `.secret` file if it cannot find one +and write it to the Rails root. In the omnibus packages, reconfigure writes the +`.secret` file first, so that GitLab never tries to write it. + +## Code, data and logs are in separate directories + +The omnibus design separates code (read-only, under `/opt/gitlab`) from data +(read/write, under `/var/opt/gitlab`) and logs (read/write, under +`/var/log/gitlab`). To make this happen the reconfigure script sets custom +paths where it can in GitLab config files, and where there are no path +settings, it uses symlinks. + +For example, `config/gitlab.yml` is treated as data so that file is a symlink. +The same goes for `public/uploads`. The `log/` directory is replaced by omnibus +with a symlink to `/var/log/gitlab/gitlab-rails`. diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md index 42f17e19536..821027f43fa 100644 --- a/doc/development/shell_commands.md +++ b/doc/development/shell_commands.md @@ -139,6 +139,11 @@ path = File.join(repo_path, user_input) File.read(path) ``` +If you have to use user input a relative path, prefix `./` to the path. + +Prefixing user-supplied paths also offers extra protection against paths +starting with `-` (see the discussion about using `--` above). + ## Guard against path traversal Path traversal is a security where the program (GitLab) tries to restrict user diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md new file mode 100644 index 00000000000..2f01defc11d --- /dev/null +++ b/doc/development/ui_guide.md @@ -0,0 +1,12 @@ +# UI Guide for building GitLab + +## Best practices for creating new pages in GitLab + +TODO: write some best practices when develop GitLab features. + +## GitLab UI development kit + +We created a page inside GitLab where you can check commonly used html and css elements. + +When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples +you can use during GitLab development. diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md index 00867ead80d..f7d4f3de68b 100644 --- a/doc/hooks/custom_hooks.md +++ b/doc/hooks/custom_hooks.md @@ -24,7 +24,7 @@ set up a custom hook. 1. Pick a project that needs a custom git hook. 1. On the GitLab server, navigate to the project's repository directory. -For a manual install the path is usually +For an installation from source the path is usually `/home/git/repositories/<group>/<project>.git`. For Omnibus installs the path is usually `/var/opt/gitlab/git-data/repositories/<group>/<project>.git`. 1. Create a new directory in this location called `custom_hooks`. diff --git a/doc/install/installation.md b/doc/install/installation.md index bd81073c7eb..2b204c72476 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -1,8 +1,8 @@ -# Installation +# Installation from source ## Consider the Omnibus package installation -Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm). +Since an installation from source is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm). ## Select Version to Install @@ -141,7 +141,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Try connecting to the new database with the new user sudo -u git -H psql -d gitlabhq_production - + # Quit the database session gitlabhq_production> \q @@ -183,9 +183,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-6-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-8-stable gitlab -**Note:** You can change `7-6-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `7-8-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It @@ -280,7 +280,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.4.2] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.5.4] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 2cf9e82fd21..5bdb9caa2bf 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -22,7 +22,7 @@ For the installations options please see [the installation page on the GitLab we - FreeBSD On the above unsupported distributions is still possible to install GitLab yourself. -Please see the [manual installation guide](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) and the [unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) on the public wiki for more information. +Please see the [installation from source guide](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md) and the [unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) on the public wiki for more information. ### Non-Unix operating systems such as Windows diff --git a/doc/integration/README.md b/doc/integration/README.md index 0087167bb84..286bd34a0bd 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -6,10 +6,12 @@ See the documentation below for details on how to configure these services. - [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc. - [LDAP](ldap.md) Set up sign in via LDAP -- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, and Google via OAuth. +- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab, and Google via OAuth. - [Slack](slack.md) Integrate with the Slack chat service +- [OAuth2 provider](oauth_provider.md) OAuth2 application creation +- [Gmail](gitlab_buttons_in_gmail.md) Adds GitLab actions to messages -Jenkins support is [available in GitLab EE](http://doc.gitlab.com/ee/integration/jenkins.html). +GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html) and [advanced Jenkins support](http://doc.gitlab.com/ee/integration/jenkins.html). ## Project services diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md new file mode 100644 index 00000000000..cc6389f5aaf --- /dev/null +++ b/doc/integration/bitbucket.md @@ -0,0 +1,121 @@ +# Integrate your server with Bitbucket + +Import projects from Bitbucket and login to your GitLab instance with your Bitbucket account. + +To enable the Bitbucket OmniAuth provider you must register your application with Bitbucket. +Bitbucket will generate an application ID and secret key for you to use. + +1. Sign in to Bitbucket. + +1. Navigate to your individual user settings or a team's settings, depending on how you want the application registered. It does not matter if the application is registered as an individual or a team - that is entirely up to you. + +1. Select "OAuth" in the left menu. + +1. Select "Add consumer". + +1. Provide the required details. + - Name: This can be anything. Consider something like "\<Organization\>'s GitLab" or "\<Your Name\>'s GitLab" or something else descriptive. + - Application description: Fill this in if you wish. + - URL: The URL to your GitLab installation. 'https://gitlab.company.com' +1. Select "Save". + +1. You should now see a Key and Secret in the list of OAuth customers. + Keep this page open as you continue configuration. + +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For instalations from source: + + ```sh + cd /home/git/gitlab + + sudo -u git -H editor config/gitlab.yml + ``` + +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "bitbucket", + "app_id" => "YOUR_KEY", + "app_secret" => "YOUR_APP_SECRET", + "url" => "https://bitbucket.org/" + } + ] + ``` + + For installation from source: + + ``` + - { name: 'bitbucket', app_id: 'YOUR_KEY', + app_secret: 'YOUR_APP_SECRET' } + ``` + +1. Change 'YOUR_APP_ID' to the key from the Bitbucket application page from step 7. + +1. Change 'YOUR_APP_SECRET' to the secret from the Bitbucket application page from step 7. + +1. Save the configuration file. + +1. Restart GitLab for the changes to take effect. + +On the sign in page there should now be a Bitbucket icon below the regular sign in form. +Click the icon to begin the authentication process. Bitbucket will ask the user to sign in and authorize the GitLab application. +If everything goes well the user will be returned to GitLab and will be signed in. + +## Bitbucket project import + +To allow projects to be imported directly into GitLab, Bitbucket requires two extra setup steps compared to GitHub and GitLab.com. + +Bitbucket doesn't allow OAuth applications to clone repositories over HTTPS, and instead requires GitLab to use SSH and identify itself using your GitLab server's SSH key. + +### Step 1: Known hosts + +To allow GitLab to connect to Bitbucket over SSH, you need to add 'bitbucket.org' to your GitLab server's known SSH hosts. Take the following steps to do so: + +1. Manually connect to 'bitbucket.org' over SSH, while logged in as the `git` account that GitLab will use: + + ```sh + ssh git@bitbucket.org + ``` + +1. Verify the RSA key fingerprint you'll see in the response matches the one in the [Bitbucket documentation](https://confluence.atlassian.com/display/BITBUCKET/Use+the+SSH+protocol+with+Bitbucket#UsetheSSHprotocolwithBitbucket-KnownhostorBitbucket'spublickeyfingerprints) (the specific IP address doesn't matter): + + ```sh + The authenticity of host 'bitbucket.org (207.223.240.182)' can't be established. + RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40. + Are you sure you want to continue connecting (yes/no)? + ``` + +1. If the fingerprint matches, type `yes` to continue connecting and have 'bitbucket.org' be added to your known hosts. + +1. Your GitLab server is now able to connect to Bitbucket over SSH. Continue to step 2: + +### Step 2: Public key + +To be able to access repositories on Bitbucket, GitLab will automatically register your public key with Bitbucket as a deploy key for the repositories to be imported. Your public key needs to be at `~/.ssh/id_rsa.pub`, which will expand to `/home/git/.ssh/id_rsa.pub` in most configurations. + +If you have that file in place, you're all set and should see the "Import projects from Bitbucket" option enabled. If you don't, do the following: + +1. Create a new SSH key: + + ```sh + sudo -u git -H ssh-keygen + ``` + + Make sure to use an **empty passphrase**. + +2. Restart GitLab to allow it to find the new public key. + +You should now see the "Import projects from Bitbucket" option on the New Project page enabled. diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index ba4df9f8fe0..96755707dee 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -1,13 +1,39 @@ # External issue tracker -GitLab has a great issue tracker but you can also use an external issue tracker such as JIRA, Bugzilla or Redmine. This is something that you can turn on per GitLab project. If for example you configure JIRA it provides the following functionality: +GitLab has a great issue tracker but you can also use an external issue tracker such as Jira, Bugzilla or Redmine. You can configure issue trackers per GitLab project. For instance, if you configure Jira it allows you to do the following: -- the 'Issues' link on the GitLab project pages takes you to the appropriate JIRA issue index; -- clicking 'New issue' on the project dashboard creates a new JIRA issue; -- To reference JIRA issue PROJECT-1234 in comments, use syntax PROJECT-1234. Commit messages get turned into HTML links to the corresponding JIRA issue. +- the 'Issues' link on the GitLab project pages takes you to the appropriate Jira issue index; +- clicking 'New issue' on the project dashboard creates a new Jira issue; +- To reference Jira issue PROJECT-1234 in comments, use syntax PROJECT-1234. Commit messages get turned into HTML links to the corresponding Jira issue.  -You can configure the integration in the gitlab.yml configuration file. +GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html). + +## Configuration + +### Project Service + +You can enable an external issue tracker per project. As an example, we will configure `Redmine` for project named gitlab-ci. + +Fill in the required details on the page: + + + +* `description` A name for the issue tracker (to differentiate between instances, for example). +* `project_url` The URL to the project in Redmine which is being linked to this GitLab project. +* `issues_url` The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the url. This id is used by GitLab as a placeholder to replace the issue number. +* `new_issue_url` This is the URL to create a new issue in Redmine for the project linked to this GitLab project. + +### Service Template + +It is necessary to configure the external issue tracker per project, because project specific details are needed for the integration with GitLab. +The admin can add a service template that sets a default for each project. This makes it much easier to configure individual projects. + +In GitLab Admin section, navigate to `Service Templates` and choose the service template you want to create: + + + +After the template is created, the template details will be pre-filled on the project service page. Support to add your commits to the Jira ticket automatically is [available in GitLab EE](http://doc.gitlab.com/ee/integration/jira.html). diff --git a/doc/integration/github.md b/doc/integration/github.md index a586334b98d..b64501c2aaa 100644 --- a/doc/integration/github.md +++ b/doc/integration/github.md @@ -1,6 +1,9 @@ -# GitHub OAuth2 OmniAuth Provider +# Integrate your server with GitHub -To enable the GitHub OmniAuth provider you must register your application with GitHub. GitHub will generate a client ID and secret key for you to use. +Import projects from GitHub and login to your GitLab instance with your GitHub account. + +To enable the GitHub OmniAuth provider you must register your application with GitHub. +GitHub will generate an application ID and secret key for you to use. 1. Sign in to GitHub. @@ -17,32 +20,60 @@ To enable the GitHub OmniAuth provider you must register your application with G - Authorization callback URL: 'https://gitlab.company.com/' 1. Select "Register application". -1. You should now see a Client ID and Client Secret near the top right of the page (see screenshot). Keep this page open as you continue configuration.  +1. You should now see a Client ID and Client Secret near the top right of the page (see screenshot). + Keep this page open as you continue configuration. +  1. On your GitLab server, open the configuration file. + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For instalations from source: + ```sh - cd /home/git/gitlab + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml + sudo -u git -H editor config/gitlab.yml ``` -1. Find the section dealing with OmniAuth. See [Initial OmniAuth Configuration](README.md#initial-omniauth-configuration) for more details. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "github", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "url" => "https://github.com/", + "args" => { "scope" => "user:email" } + } + ] + ``` -1. Under `providers:` uncomment (or add) lines that look like the following: + For installation from source: ``` - - { name: 'github', app_id: 'YOUR APP ID', - app_secret: 'YOUR APP SECRET', - args: { scope: 'user:email' } } + - { name: 'github', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + args: { scope: 'user:email' } } ``` -1. Change 'YOUR APP ID' to the client ID from the GitHub application page from step 7. +1. Change 'YOUR_APP_ID' to the client ID from the GitHub application page from step 7. -1. Change 'YOUR APP SECRET' to the client secret from the GitHub application page from step 7. +1. Change 'YOUR_APP_SECRET' to the client secret from the GitHub application page from step 7. 1. Save the configuration file. 1. Restart GitLab for the changes to take effect. -On the sign in page there should now be a GitHub icon below the regular sign in form. Click the icon to begin the authentication process. GitHub will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in. +On the sign in page there should now be a GitHub icon below the regular sign in form. +Click the icon to begin the authentication process. GitHub will ask the user to sign in and authorize the GitLab application. +If everything goes well the user will be returned to GitLab and will be signed in. diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md index b3b1d897225..216f1f11a9b 100644 --- a/doc/integration/gitlab.md +++ b/doc/integration/gitlab.md @@ -1,10 +1,13 @@ -# GitLab OAuth2 OmniAuth Provider +# Integrate your server with GitLab.com -To enable the GitLab OmniAuth provider you must register your application with GitLab. GitLab will generate a client ID and secret key for you to use. +Import projects from GitLab.com and login to your GitLab instance with your GitLab.com account. -1. Sign in to GitLab. +To enable the GitLab.com OmniAuth provider you must register your application with GitLab.com. +GitLab.com will generate an application ID and secret key for you to use. -1. Navigate to your settings. +1. Sign in to GitLab.com + +1. Navigate to your profile settings. 1. Select "Applications" in the left menu. @@ -12,43 +15,70 @@ To enable the GitLab OmniAuth provider you must register your application with G 1. Provide the required details. - Name: This can be anything. Consider something like "\<Organization\>'s GitLab" or "\<Your Name\>'s GitLab" or something else descriptive. - - Redirect URI: - + - Redirect URI: + ``` - http://gitlab.example.com/import/gitlab/callback - http://gitlab.example.com/users/auth/gitlab/callback + http://your-gitlab.example.com/import/gitlab/callback + http://your-gitlab.example.com/users/auth/gitlab/callback ``` - The first link is required for the importer and second for the authorization. + The first link is required for the importer and second for the authorization. 1. Select "Submit". -1. You should now see a Application ID and Secret. Keep this page open as you continue configuration. +1. You should now see a Client ID and Client Secret near the top right of the page (see screenshot). + Keep this page open as you continue configuration. +  1. On your GitLab server, open the configuration file. + For omnibus package: + ```sh - cd /home/git/gitlab + sudo editor /etc/gitlab/gitlab.rb + ``` + + For instalations from source: - sudo -u git -H editor config/gitlab.yml + ```sh + cd /home/git/gitlab + + sudo -u git -H editor config/gitlab.yml ``` -1. Find the section dealing with OmniAuth. See [Initial OmniAuth Configuration](README.md#initial-omniauth-configuration) for more details. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "gitlab", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "args" => { "scope" => "api" } + } + ] + ``` -1. Under `providers:` uncomment (or add) lines that look like the following: + For installations from source: ``` - - { name: 'gitlab', app_id: 'YOUR APP ID', - app_secret: 'YOUR APP SECRET', - args: { scope: 'api' } } + - { name: 'gitlab', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + args: { scope: 'api' } } ``` -1. Change 'YOUR APP ID' to the Application ID from the GitLab application page. +1. Change 'YOUR_APP_ID' to the Application ID from the GitLab.com application page. -1. Change 'YOUR APP SECRET' to the secret from the GitLab application page. +1. Change 'YOUR_APP_SECRET' to the secret from the GitLab.com application page. 1. Save the configuration file. 1. Restart GitLab for the changes to take effect. -On the sign in page there should now be a GitLab icon below the regular sign in form. Click the icon to begin the authentication process. GitLab will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to your GitLab instance and will be signed in. +On the sign in page there should now be a GitLab.com icon below the regular sign in form. +Click the icon to begin the authentication process. GitLab.com will ask the user to sign in and authorize the GitLab application. +If everything goes well the user will be returned to your GitLab instance and will be signed in. diff --git a/doc/integration/gitlab_app.png b/doc/integration/gitlab_app.png Binary files differnew file mode 100644 index 00000000000..3f9391a821b --- /dev/null +++ b/doc/integration/gitlab_app.png diff --git a/doc/integration/google.md b/doc/integration/google.md index 7a78aff8ea4..e1c14c7c948 100644 --- a/doc/integration/google.md +++ b/doc/integration/google.md @@ -27,27 +27,50 @@ To enable the Google OAuth2 OmniAuth provider you must register your application - Authorized redirect URI: 'https://gitlab.example.com/users/auth/google_oauth2/callback' 1. Under the heading "Client ID for web application" you should see a Client ID and Client secret (see screenshot). Keep this page open as you continue configuration.  -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For instalations from source: ```sh - cd /home/git/gitlab + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml + sudo -u git -H editor config/gitlab.yml ``` -1. Find the section dealing with OmniAuth. See [Initial OmniAuth Configuration](README.md#initial-omniauth-configuration) for more details. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "google_oauth2", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "args" => { "access_type" => "offline", "approval_prompt" => '' } + } + ] + ``` -1. Under `providers:` uncomment (or add) lines that look like the following: + For installations from source: ``` - - { name: 'google_oauth2', app_id: 'YOUR APP ID', - app_secret: 'YOUR APP SECRET', - args: { access_type: 'offline', approval_prompt: '' } } + - { name: 'google_oauth2', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + args: { access_type: 'offline', approval_prompt: '' } } ``` -1. Change 'YOUR APP ID' to the client ID from the GitHub application page from step 7. +1. Change 'YOUR_APP_ID' to the client ID from the Google Developer page from step 10. -1. Change 'YOUR APP SECRET' to the client secret from the GitHub application page from step 7. +1. Change 'YOUR_APP_SECRET' to the client secret from the Google Developer page from step 10. 1. Save the configuration file. diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md new file mode 100644 index 00000000000..192c321f712 --- /dev/null +++ b/doc/integration/oauth_provider.md @@ -0,0 +1,35 @@ +## GitLab as OAuth2 authentication service provider + +This document is about using GitLab as an OAuth authentication service provider to sign into other services. +If you want to use other OAuth authentication service providers to sign into GitLab please see the [OAuth2 client documentation](../api/oauth2.md) + +OAuth2 provides client applications a 'secure delegated access' to server resources on behalf of a resource owner. Or you can allow users to sign in to your application with their GitLab.com account. +In fact OAuth allows to issue access token to third-party clients by an authorization server, +with the approval of the resource owner, or end-user. +Mostly, OAuth2 is using for SSO (Single sign-on). But you can find a lot of different usages for this functionality. +For example, our feature 'GitLab Importer' is using OAuth protocol to give an access to repositories without sharing user credentials to GitLab.com account. +Also GitLab.com application can be used for authentication to your GitLab instance if needed [GitLab OmniAuth](gitlab.md). + +GitLab has two ways to add new OAuth2 application to an instance, you can add application as regular user and through admin area. So GitLab actually can have an instance-wide and a user-wide applications. There is no defferences between them except the different permission levels. + +### Adding application through profile +Go to your profile section 'Application' and press button 'New Application' + + + +After this you will see application form, where "Name" is arbitrary name, "Redirect URI" is URL in your app where users will be sent after authorization on GitLab.com. + + + +### Authorized application +Every application you authorized will be shown in your "Authorized application" sections. + + + +At any time you can revoke access just clicking button "Revoke" + +### OAuth applications in admin area + +If you want to create application that does not belong to certain user you can create it from admin area + +
\ No newline at end of file diff --git a/doc/integration/oauth_provider/admin_application.png b/doc/integration/oauth_provider/admin_application.png Binary files differnew file mode 100644 index 00000000000..a5f34512aa8 --- /dev/null +++ b/doc/integration/oauth_provider/admin_application.png diff --git a/doc/integration/oauth_provider/application_form.png b/doc/integration/oauth_provider/application_form.png Binary files differnew file mode 100644 index 00000000000..ae135db2627 --- /dev/null +++ b/doc/integration/oauth_provider/application_form.png diff --git a/doc/integration/oauth_provider/authorized_application.png b/doc/integration/oauth_provider/authorized_application.png Binary files differnew file mode 100644 index 00000000000..d3ce05be9cc --- /dev/null +++ b/doc/integration/oauth_provider/authorized_application.png diff --git a/doc/integration/oauth_provider/user_wide_applications.png b/doc/integration/oauth_provider/user_wide_applications.png Binary files differnew file mode 100644 index 00000000000..719e1974068 --- /dev/null +++ b/doc/integration/oauth_provider/user_wide_applications.png diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index 15b4fb622af..24f7b4bb4b4 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -1,8 +1,8 @@ # OmniAuth -GitLab leverages OmniAuth to allow users to sign in using Twitter, GitHub, and other popular services. Configuring +GitLab leverages OmniAuth to allow users to sign in using Twitter, GitHub, and other popular services. -OmniAuth does not prevent standard GitLab authentication or LDAP (if configured) from continuing to work. Users can choose to sign in using any of the configured mechanisms. +Configuring OmniAuth does not prevent standard GitLab authentication or LDAP (if configured) from continuing to work. Users can choose to sign in using any of the configured mechanisms. - [Initial OmniAuth Configuration](#initial-omniauth-configuration) - [Supported Providers](#supported-providers) @@ -11,9 +11,37 @@ OmniAuth does not prevent standard GitLab authentication or LDAP (if configured) ## Initial OmniAuth Configuration -Before configuring individual OmniAuth providers there are a few global settings that need to be verified. +Before configuring individual OmniAuth providers there are a few global settings that are in common for all providers that we need to consider. -1. Open the configuration file. +- Omniauth needs to be enabled, see details below for example. +- `allow_single_sign_on` defaults to `false`. If `false` users must be created manually or they will not be able to +sign in via OmniAuth. +- `block_auto_created_users` defaults to `true`. If `true` auto created users will be blocked by default and will +have to be unblocked by an administrator before they are able to sign in. +- **Note:** If you set `allow_single_sign_on` to `true` and `block_auto_created_users` to `false` please be aware +that any user on the Internet will be able to successfully sign in to your GitLab without administrative approval. + +If you want to change these settings: + +* **For omnibus package** + + Open the configuration file: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + and change + + ``` + gitlab_rails['omniauth_enabled'] = true + gitlab_rails['omniauth_allow_single_sign_on'] = false + gitlab_rails['block_auto_created_users'] = true + ``` + +* **For installations from source** + + Open the configuration file: ```sh cd /home/git/gitlab @@ -21,13 +49,13 @@ Before configuring individual OmniAuth providers there are a few global settings sudo -u git -H editor config/gitlab.yml ``` -1. Find the section dealing with OmniAuth. The section will look similar to the following. + and change the following section ``` - ## OmniAuth settings + ## OmniAuth settings omniauth: # Allow login via Twitter, Google, etc. using OmniAuth providers - enabled: false + enabled: true # CAUTION! # This allows users to login without having a user account first (default: false). @@ -35,47 +63,15 @@ Before configuring individual OmniAuth providers there are a few global settings allow_single_sign_on: false # Locks down those users until they have been cleared by the admin (default: true). block_auto_created_users: true - - ## Auth providers - # Uncomment the following lines and fill in the data of the auth provider you want to use - # If your favorite auth provider is not listed you can use others: - # see https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations - # The 'app_id' and 'app_secret' parameters are always passed as the first two - # arguments, followed by optional 'args' which can be either a hash or an array. - providers: - # - { name: 'google_oauth2', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET', - # args: { access_type: 'offline', approval_prompt: '' } } - # - { name: 'twitter', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET'} - # - { name: 'github', app_id: 'YOUR APP ID', - # app_secret: 'YOUR APP SECRET', - # args: { scope: 'user:email' } } - # - {"name": 'shibboleth', - # args: { shib_session_id_field: "HTTP_SHIB_SESSION_ID", - # shib_application_id_field: "HTTP_SHIB_APPLICATION_ID", - # uid_field: "HTTP_EPPN", - # name_field: "HTTP_CN", - # info_fields: {"email": "HTTP_MAIL" } } } - ``` -1. Change `enabled` to `true`. - -1. Consider the next two configuration options: `allow_single_sign_on` and `block_auto_created_users`. - - - `allow_single_sign_on` defaults to `false`. If `false` users must be created manually or they will not be able to - sign in via OmniAuth. - - `block_auto_created_users` defaults to `true`. If `true` auto created users will be blocked by default and will - have to be unblocked by an administrator before they are able to sign in. - - **Note:** If you set `allow_single_sign_on` to `true` and `block_auto_created_users` to `false` please be aware - that any user on the Internet will be able to successfully sign in to your GitLab without administrative approval. - -1. Choose one or more of the Supported Providers below to continue configuration. +Now we can choose one or more of the Supported Providers below to continue configuration. ## Supported Providers - [GitHub](github.md) +- [Bitbucket](bitbucket.md) +- [GitLab.com](gitlab.md) - [Google](google.md) - [Shibboleth](shibboleth.md) - [Twitter](twitter.md) diff --git a/doc/integration/redmine_configuration.png b/doc/integration/redmine_configuration.png Binary files differnew file mode 100644 index 00000000000..6b145363229 --- /dev/null +++ b/doc/integration/redmine_configuration.png diff --git a/doc/integration/redmine_service_template.png b/doc/integration/redmine_service_template.png Binary files differnew file mode 100644 index 00000000000..1159eb5b964 --- /dev/null +++ b/doc/integration/redmine_service_template.png diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md index ea11f1afeab..6258e5f1030 100644 --- a/doc/integration/shibboleth.md +++ b/doc/integration/shibboleth.md @@ -2,12 +2,12 @@ This documentation is for enabling shibboleth with gitlab-omnibus package. -In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure Nginx that is bundled in gitlab-omnibus package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider. +In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure Nginx that is bundled in gitlab-omnibus package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider. To enable the Shibboleth OmniAuth provider you must: -1. Configure Apache shibboleth module. Installation and configuration of module it self is out of scope of this document. +1. Configure Apache shibboleth module. Installation and configuration of module it self is out of scope of this document. Check https://wiki.shibboleth.net/ for more info. 1. You can find Apache config in gitlab-recipes (https://github.com/gitlabhq/gitlab-recipes/blob/master/web-server/apache/gitlab-ssl.conf) @@ -37,15 +37,15 @@ exclude shibboleth URLs from rewriting, add "RewriteCond %{REQUEST_URI} !/Shibbo # Apache equivalent of Nginx try files RewriteEngine on RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_URI} !/Shibboleth.sso - RewriteCond %{REQUEST_URI} !/shibboleth-sp + RewriteCond %{REQUEST_URI} !/Shibboleth.sso + RewriteCond %{REQUEST_URI} !/shibboleth-sp RewriteRule .* http://127.0.0.1:8080%{REQUEST_URI} [P,QSA] RequestHeader set X_FORWARDED_PROTO 'https' ``` -1. Edit /etc/gitlab/gitlab.rb configuration file, your shibboleth attributes should be in form of "HTTP_ATTRIBUTE" and you should addjust them to your need and environment. Add any other configuration you need. +1. Edit /etc/gitlab/gitlab.rb configuration file, your shibboleth attributes should be in form of "HTTP_ATTRIBUTE" and you should addjust them to your need and environment. Add any other configuration you need. -File it should look like this: +File should look like this: ``` external_url 'https://gitlab.example.com' gitlab_rails['internal_api_url'] = 'https://gitlab.example.com' @@ -70,7 +70,7 @@ gitlab_rails['omniauth_providers'] = [ ] ``` -1. Save changes and reconfigure gitlab: +1. Save changes and reconfigure gitlab: ``` sudo gitlab-ctl reconfigure ``` diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md index b9e501c5ec1..fe9091ad9a8 100644 --- a/doc/integration/twitter.md +++ b/doc/integration/twitter.md @@ -33,25 +33,46 @@ To enable the Twitter OmniAuth provider you must register your application with 1. On your GitLab server, open the configuration file. + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For instalations from source: + ```sh - cd /home/git/gitlab + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml + sudo -u git -H editor config/gitlab.yml ``` -1. Find the section dealing with OmniAuth. See [Initial OmniAuth Configuration](README.md#initial-omniauth-configuration) -for more details. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "twitter", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET" + } + ] + ``` -1. Under `providers:` uncomment (or add) lines that look like the following: + For installations from source: ``` - - { name: 'twitter', app_id: 'YOUR APP ID', - app_secret: 'YOUR APP SECRET' } + - { name: 'twitter', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET' } ``` -1. Change 'YOUR APP ID' to the API key from Twitter page in step 11. +1. Change 'YOUR_APP_ID' to the API key from Twitter page in step 11. -1. Change 'YOUR APP SECRET' to the API secret from the Twitter page in step 11. +1. Change 'YOUR_APP_SECRET' to the API secret from the Twitter page in step 11. 1. Save the configuration file. diff --git a/doc/logs/logs.md b/doc/logs/logs.md new file mode 100644 index 00000000000..ec0109a426f --- /dev/null +++ b/doc/logs/logs.md @@ -0,0 +1,102 @@ +## Log system +GitLab has advanced log system so everything is logging and you can analize your instance using various system log files. +In addition to system log files, GitLab Enterprise Edition comes with Audit Events. Find more about them [in Audit Events documentation](http://doc.gitlab.com/ee/administration/audit_events.html) + +System log files are typically plain text in a standard log file format. This guide talks about how to read and use these system log files. + +#### production.log +This file lives in `/var/log/gitlab/gitlab-rails/production.log` for omnibus package or in `/home/git/gitlab/logs/production.log` for installations from the source. + +This file contains information about all performed requests. You can see url and type of request, IP address and what exactly parts of code were involved to service this particular request. Also you can see all SQL request that have been performed and how much time it took. +This task is more useful for GitLab contributors and developers. Use part of this log file when you are going to report bug. + +``` +Started GET "/gitlabhq/yaml_db/tree/master" for 168.111.56.1 at 2015-02-12 19:34:53 +0200 +Processing by Projects::TreeController#show as HTML + Parameters: {"project_id"=>"gitlabhq/yaml_db", "id"=>"master"} + + ... [CUT OUT] + + amespaces"."created_at" DESC, "namespaces"."id" DESC LIMIT 1[0m [["id", 26]] + [1m[35mCACHE (0.0ms)[0m SELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members"."type" IN ('ProjectMember') AND "members"."source_id" = $1 AND "members"."source_type" = $2 AND "members"."user_id" = 1 ORDER BY "members"."created_at" DESC, "members"."id" DESC LIMIT 1 [["source_id", 18], ["source_type", "Project"]] + [1m[36mCACHE (0.0ms)[0m [1mSELECT "members".* FROM "members" WHERE "members"."source_type" = 'Project' AND "members". + [1m[36m (1.4ms)[0m [1mSELECT COUNT(*) FROM "merge_requests" WHERE "merge_requests"."target_project_id" = $1 AND ("merge_requests"."state" IN ('opened','reopened'))[0m [["target_project_id", 18]] + Rendered layouts/nav/_project.html.haml (28.0ms) + Rendered layouts/_collapse_button.html.haml (0.2ms) + Rendered layouts/_flash.html.haml (0.1ms) + Rendered layouts/_page.html.haml (32.9ms) +Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms) +``` +In this example we can see that server processed HTTP request with url `/gitlabhq/yaml_db/tree/master` from IP 168.111.56.1 at 2015-02-12 19:34:53 +0200. Also we can see that request was processed by Projects::TreeController. + +#### application.log +This file lives in `/var/log/gitlab/gitlab-rails/application.log` for omnibus package or in `/home/git/gitlab/logs/application.log` for installations from the source. + +This log file helps you discover events happening in your instance such as user creation, project removing and so on. + +``` +October 06, 2014 11:56: User "Administrator" (admin@example.com) was created +October 06, 2014 11:56: Documentcloud created a new project "Documentcloud / Underscore" +October 06, 2014 11:56: Gitlab Org created a new project "Gitlab Org / Gitlab Ce" +October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk) was removed +October 07, 2014 11:25: Project "project133" was removed +``` +#### githost.log +This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for omnibus package or in `/home/git/gitlab/logs/githost.log` for installations from the source. + +The GitLab has to interact with git repositories but in some rare cases something can go wrong and in this case you will know what exactly happened. This log file contains all failed requests from GitLab to git repository. In majority of cases this file will be useful for developers only. +``` +December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq/.git --work-tree=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq merge --no-ff -mMerge branch 'feature_conflict' into 'feature' source/feature_conflict + +error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git' +``` + +#### satellites.log +This file lives in `/var/log/gitlab/gitlab-rails/satellites.log` for omnibus package or in `/home/git/gitlab/logs/satellites.log` for installations from the source. + +In some cases GitLab should perform write actions to git repository, for example when it is needed to merge the merge request or edit a file with online editor. If something went wrong you can look into this file to find out what exactly happened. +``` +October 07, 2014 11:36: Failed to create satellite for Chesley Weimann III / project1817 +October 07, 2014 11:36: PID: 1872: git clone /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/conrad6841/gitlabhq +October 07, 2014 11:36: PID: 1872: -> fatal: repository '/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git' does not exist +``` + +#### sidekiq.log +This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for omnibus package or in `/home/git/gitlab/logs/sidekiq.log` for installations from the source. + +GitLab uses background jobs for processing tasks which can take a long time. All information about processing these jobs are writing down to this file. +``` +2014-06-10T07:55:20Z 2037 TID-tm504 ERROR: /opt/bitnami/apps/discourse/htdocs/vendor/bundle/ruby/1.9.1/gems/redis-3.0.7/lib/redis/client.rb:228:in `read' +2014-06-10T18:18:26Z 14299 TID-55uqo INFO: Booting Sidekiq 3.0.0 with redis options {:url=>"redis://localhost:6379/0", :namespace=>"sidekiq"} +``` + +#### gitlab-shell.log +This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for omnibus package or in `/home/git/gitlab-shell/logs/sidekiq.log` for installations from the source. + +gitlab-shell is using by Gitlab for executing git commands and provide ssh access to git repositories. + +``` +I, [2015-02-13T06:17:00.671315 #9291] INFO -- : Adding project root/example.git at </var/opt/gitlab/git-data/repositories/root/dcdcdcdcd.git>. +I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory and simlinking global hooks directory for /var/opt/gitlab/git-data/repositories/root/example.git. +``` + +#### unicorn_stderr.log +This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for omnibus package or in `/home/git/gitlab/logs/unicorn_stderr.log` for installations from the source. + +Unicorn is a high-performance forking Web server which is used for serving GitLab application. You can look at this log, for example, if your application does not respond. This log cantains all information about state of unicorn processes at any given time. + +``` +I, [2015-02-13T06:14:46.680381 #9047] INFO -- : Refreshing Gem list +I, [2015-02-13T06:14:56.931002 #9047] INFO -- : listening on addr=127.0.0.1:8080 fd=12 +I, [2015-02-13T06:14:56.931381 #9047] INFO -- : listening on addr=/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket fd=13 +I, [2015-02-13T06:14:56.936638 #9047] INFO -- : master process ready +I, [2015-02-13T06:14:56.946504 #9092] INFO -- : worker=0 spawned pid=9092 +I, [2015-02-13T06:14:56.946943 #9092] INFO -- : worker=0 ready +I, [2015-02-13T06:14:56.947892 #9094] INFO -- : worker=1 spawned pid=9094 +I, [2015-02-13T06:14:56.948181 #9094] INFO -- : worker=1 ready +W, [2015-02-13T07:16:01.312916 #9094] WARN -- : #<Unicorn::HttpServer:0x0000000208f618>: worker (pid: 9094) exceeds memory limit (320626688 bytes > 247066940 bytes) +W, [2015-02-13T07:16:01.313000 #9094] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 9094) alive: 3621 sec (trial 1) +I, [2015-02-13T07:16:01.530733 #9047] INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1 +I, [2015-02-13T07:16:01.534501 #13379] INFO -- : worker=1 spawned pid=13379 +I, [2015-02-13T07:16:01.534848 #13379] INFO -- : worker=1 ready +``` diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index cc6b5d73396..64f28d46451 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -140,25 +140,25 @@ But let's throw in a <b>tag</b>. ## Emoji - Sometimes you want to be a :ninja: and add some :glowing_star: to your :speech_balloon:. Well we have a gift for you: + Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you: - :high_voltage_sign: You can use emoji anywhere GFM is supported. :victory_hand: + :zap: You can use emoji anywhere GFM is supported. :v: - You can use it to point out a :bug: or warn about :speak_no_evil_monkey: patches. And if someone improves your really :snail: code, send them some :cake:. People will :heart: you for that. + You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that. - If you are new to this, don't be :fearful_face:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes. + If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes. - Consult the [Emoji Cheat Sheet](https://www.dropbox.com/s/b9xaqb977s6d8w1/cheat_sheet.pdf) for a list of all supported emoji codes. :thumbsup: + Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup: -Sometimes you want to be a :ninja: and add some :glowing_star: to your :speech_balloon:. Well we have a gift for you: +Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you: -:high_voltage_sign: You can use emoji anywhere GFM is supported. :victory_hand: +:zap: You can use emoji anywhere GFM is supported. :v: -You can use it to point out a :bug: or warn about :speak_no_evil_monkey: patches. And if someone improves your really :snail: code, send them some :cake:. People will :heart: you for that. +You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that. -If you are new to this, don't be :fearful_face:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes. +If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes. -Consult the [Emoji Cheat Sheet](https://www.dropbox.com/s/b9xaqb977s6d8w1/cheat_sheet.pdf) for a list of all supported emoji codes. :thumbsup: +Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup: ## Special GitLab References diff --git a/doc/project_services/irker.md b/doc/project_services/irker.md new file mode 100644 index 00000000000..780a45bca20 --- /dev/null +++ b/doc/project_services/irker.md @@ -0,0 +1,46 @@ +# Irker IRC Gateway + +GitLab provides a way to push update messages to an Irker server. When +configured, pushes to a project will trigger the service to send data directly +to the Irker server. + +See the project homepage for further info: http://www.catb.org/esr/irker/ + +## Needed setup + +You will first need an Irker daemon. You can download the Irker code from its +gitorious repository on https://gitorious.org/irker: `git clone +git@gitorious.org:irker/irker.git`. Once you have downloaded the code, you can +run the python script named `irkerd`. This script is the gateway script, it acts +both as an IRC client, for sending messages to an IRC server obviously, and as a +TCP server, for receiving messages from the GitLab service. + +If the Irker server runs on the same machine, you are done. If not, you will +need to follow the firsts steps of the next section. + +## Optional setup + +In the `app/models/project_services/irker_service.rb` file, you can modify some +options in the `initialize_settings` method: +- **server_ip** (defaults to `localhost`): the server IP address where the +`irkerd` daemon runs; +- **server_port** (defaults to `6659`): the server port of the `irkerd` daemon; +- **max_channels** (defaults to `3`): the maximum number of recipients the +client is authorized to join, per project; +- **default_irc_uri** (no default) : if this option is set, it has to be in the +format `irc[s]://domain.name` and will be prepend to each and every channel +provided by the user which is not a full URI. + +If the Irker server and the GitLab application do not run on the same host, you +will **need** to setup at least the **server_ip** option. + +## Note on Irker recipients + +Irker accepts channel names of the form `chan` and `#chan`, both for the +`#chan` channel. If you want to send messages in query, you will need to add +`,isnick` avec the channel name, in this form: `Aorimn,isnick`. In this latter +case, `Aorimn` is treated as a nick and no more as a channel name. + +Irker can also join password-protected channels. Users need to append +`?key=thesecretpassword` to the chan name. + diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md index 93a57485cfd..86eda341d6c 100644 --- a/doc/project_services/project_services.md +++ b/doc/project_services/project_services.md @@ -13,6 +13,7 @@ __Project integrations with external services for continuous integration and mor - Gemnasium - GitLab CI - HipChat +- [Irker](irker.md) An IRC gateway to receive messages on repository updates. - Pivotal Tracker - Pushover - Slack diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index bbcf395c745..99cdfff0ac6 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -137,7 +137,7 @@ with the name of your bucket: Please be informed that a backup does not store your configuration files. If you use an Omnibus package please see the [instructions in the readme to backup your configuration](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#backup-and-restore-omnibus-gitlab-configuration). If you have a cookbook installation there should be a copy of your configuration in Chef. -If you have a manual installation please consider backing up your `gitlab.yml` file, any SSL keys and certificates, and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). +If you have an installation from source, please consider backing up your `gitlab.yml` file, any SSL keys and certificates, and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). ## Restore a previously created backup diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md index 9a10c8d6850..8a38937062e 100644 --- a/doc/raketasks/import.md +++ b/doc/raketasks/import.md @@ -13,7 +13,7 @@ - For omnibus-gitlab, it is located at: `/var/opt/gitlab/git-data/repositories` by default, unless you changed it in the `/etc/gitlab/gitlab.rb` file. -- For manual installations, it is usually located at: `/home/git/repositories` or you can see where +- For installations from source, it is usually located at: `/home/git/repositories` or you can see where your repositories are located by looking at `config/gitlab.yml` under the `gitlab_shell => repos_path` entry. New folder needs to have git user ownership and read/write/execute access for git user and its group: @@ -47,7 +47,7 @@ with `/home/git`. $ sudo gitlab-rake gitlab:import:repos ``` -#### Manual Installation +#### Installation from source Before running this command you need to change the directory to where your GitLab installation is located: diff --git a/doc/release/howto_rc1.md b/doc/release/howto_rc1.md index c4156d25d5f..07c703142d4 100644 --- a/doc/release/howto_rc1.md +++ b/doc/release/howto_rc1.md @@ -27,7 +27,7 @@ Make sure the code quality indicators are green / good. - [](https://coveralls.io/r/gitlabhq/gitlabhq) -### 4. Run release tool for CE and EE +### 4. Run release tool **Make sure EE `master` has latest changes from CE `master`** @@ -38,8 +38,8 @@ git clone git@dev.gitlab.org:gitlab/release-tools.git cd release-tools ``` -Release candidate creates stable branch from master. -So we need to sync master branch between all CE remotes. Also do same for EE. +Release candidate creates stable branch from master. +So we need to sync master branch between all CE, EE and CI remotes. ``` bundle exec rake sync @@ -53,22 +53,3 @@ bundle exec rake release["x.x.0.rc1"] Now developers can use master for merging new features. So you should use stable branch for future code changes related to release. - - -### 5. Release GitLab CI RC1 - -Add to your local `gitlab-ci/.git/config`: - -``` -[remote "public"] - url = none - pushurl = git@dev.gitlab.org:gitlab/gitlab-ci.git - pushurl = git@gitlab.com:gitlab-org/gitlab-ci.git - pushurl = git@github.com:gitlabhq/gitlab-ci.git -``` - -* Create a stable branch `x-y-stable` -* Bump VERSION to `x.y.0.rc1` -* `git tag -a v$(cat VERSION) -m "Version $(cat VERSION)"` -* `git push public x-y-stable v$(cat VERSION)` - diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 12376d36a9a..ec96be27f3f 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -15,7 +15,7 @@ This person should also make sure this document is kept up to date and issues ar ## Take vacations into account -The time is measured in weekdays to compensate for weekends. +The time is measured in weekdays to compensate for weekends. Do everything on time to prevent problems due to rush jobs or too little testing time. Make sure that you take into account any vacations of maintainers. If the release is falling behind immediately warn the team. @@ -38,35 +38,38 @@ Xth: (7 working days before the 22nd) Xth: (6 working days before the 22nd) - [ ] Merge CE master in to EE master via merge request (#LINK) -- [ ] Create CE, EE, CI RC1 versions (#LINK) - [ ] Determine QA person and notify this person +- [ ] Check the tasks in [how to rc1 guide](howto_rc1.md) and delegate tasks if necessary +- [ ] Create CE, EE, CI RC1 versions (#LINK) Xth: (5 working days before the 22nd) - [ ] Do QA and fix anything coming out of it (#LINK) - [ ] Close the omnibus-gitlab milestone +- [ ] Prepare the blog post (#LINK) Xth: (4 working days before the 22nd) -- [ ] Build rc1 package for GitLab.com (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#build-a-package) - [ ] Update GitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package) +- [ ] Update ci.gitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package) +- [ ] Create regression issues (CE, CI) (#LINK) +- [ ] Tweet about rc1 (#LINK) Xth: (3 working days before the 22nd) -- [ ] Create regression issues (CE, CI) (#LINK) -- [ ] Tweet about rc1 (#LINK) -- [ ] Prepare the blog post (#LINK) +- [ ] Merge CE stable branch into EE stable branch Xth: (2 working days before the 22nd) -- [ ] Merge CE stable branch into EE stable branch - [ ] Check that everyone is mentioned on the blog post (the reviewer should have done this one working day ago) +- [ ] Check that MVP is added to the mvp page (source/mvp/index.html in www-gitlab-com) Xth: (1 working day before the 22nd) - [ ] Create CE, EE, CI stable versions (#LINK) - [ ] Create Omnibus tags and build packages - [ ] Update GitLab.com with the stable version (#LINK) +- [ ] Update ci.gitLab.com with the stable version (#LINK) 22nd: @@ -87,26 +90,19 @@ asked if there is anything missing. There are three changelogs that need to be updated: CE, EE and CI. -Remove the Note text in the stable branches. - ## Create RC1 (CE, EE, CI) [Follow this How-to guide](howto_rc1.md) to create RC1. ## Prepare CHANGELOG for next release -Once the stable branches have been created, update the CHANGELOG in `master` with the upcoming version and add 70 empty -lines to it. We do this in order to avoid merge conflicts when merging the CHANGELOG. - -Make sure that the CHANGELOG im master contains the following disclaimer message: - -> Note: The upcoming release contains empty lines to reduce the number of merge conflicts, scroll down to see past releases. +Once the stable branches have been created, update the CHANGELOG in `master` with the upcoming version, usually X.X.X.pre. ## QA Create issue on dev.gitlab.org `gitlab` repository, named "GitLab X.X QA" in order to keep track of the progress. -Use the omnibus packages of Enterprise Edition using [this guide](https://dev.gitlab.org/gitlab/gitlab-ee/blob/master/doc/release/manual_testing.md). +Use the omnibus packages created for RC1 of Enterprise Edition using [this guide](https://dev.gitlab.org/gitlab/gitlab-ee/blob/master/doc/release/manual_testing.md). **NOTE** Upgrader can only be tested when tags are pushed to all repositories. Do not forget to confirm it is working before releasing. Note that in the issue. @@ -119,8 +115,7 @@ create an issue about it in order to discuss the next steps after the release. ## Update GitLab.com with RC1 -Merge the RC1 EE code into GitLab.com. -Once the build is green, create a package. +Use the omnibus EE packages created for RC1. If there are big database migrations consider testing them with the production db on a VM. Try to deploy in the morning. It is important to do this as soon as possible, so we can catch any errors before we release the full version. @@ -134,7 +129,7 @@ Please do not raise issues directly in this issue but link to issues that might The decision to create a patch release or not is with the release manager who is assigned to this issue. The release manager will comment here about the plans for patch releases. -Assign the issue to the release manager and /cc all the core-team members active on the issue tracker. If there are any known bugs in the release add them immediately. +Assign the issue to the release manager and at mention all members of gitlab core team. If there are any known bugs in the release add them immediately. ## Tweet about RC1 @@ -150,8 +145,8 @@ Tweet about the RC release: 1. Also check the CI changelog 1. Add a proposed tweet text to the blog post WIP MR description. 1. Create a WIP MR for the blog post -1. Ask Dmitriy to add screenshots to the WIP MR. -1. Decide with team who will be the MVP user. +1. Ask Dmitriy (or a team member with OS X) to add screenshots to the WIP MR. +1. Decide with core team who will be the MVP user. 1. Create WIP MR for adding MVP to MVP page on website 1. Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. 1. Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master) @@ -173,11 +168,7 @@ Bump version, create release tag and push to remotes: bundle exec rake release["x.x.0"] ``` -Also perform these steps for GitLab CI: - -1. bump version in the stable branch -1. create annotated tag -1. push the stable branch and the annotated tag to the public repositories +This will create correct version and tag and push to all CE, EE and CI remotes. Update [installation.md](/doc/install/installation.md) to the newest version in master. @@ -211,3 +202,7 @@ Consider creating a post on Hacker News. ## Update GitLab.com with the stable version - Deploy the package (should not need downtime because of the small difference with RC1) + +## Release new AMIs + +[Follow this guide](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md) diff --git a/doc/release/patch.md b/doc/release/patch.md index d8bb4aef0e2..68156ae9c0e 100644 --- a/doc/release/patch.md +++ b/doc/release/patch.md @@ -51,6 +51,8 @@ CE=false be rake release['x.x.x'] 1. [Build new packages with the latest version](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/release.md) 1. Apply the patch to GitLab.com and the private GitLab development server -1. Create and publish a blog post +1. Apply the patch to ci.gitLab.com and the private GitLab CI development server +1. Create and publish a blog post, see [patch release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/patch_release_blog_template.md) 1. Send tweets about the release from `@gitlab`, tweet should include the most important feature that the release is addressing and link to the blog post 1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only) +1. [Create new AMIs](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md) diff --git a/doc/release/security.md b/doc/release/security.md index b67e0f37a04..60bcfbb6da5 100644 --- a/doc/release/security.md +++ b/doc/release/security.md @@ -18,11 +18,13 @@ Please report suspected security vulnerabilities in private to <support@gitlab.c 1. Do the steps from [patch release document](doc/release/patch.md), starting with "Create an issue on private GitLab development server" 1. The MR with the security fix should get a 'security' label and be assigned to the release manager 1. Build the package for GitLab.com and do a deploy +1. Build the package for ci.gitLab.com and do a deploy +1. [Create new AMIs](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md) 1. Create feature branches for the blog post on GitLab.com and link them from the code branch 1. Merge and publish the blog posts 1. Send tweets about the release from `@gitlabhq` 1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq) -1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number +1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number. CVE is only needed for bugs that allow someone to own the server (Remote Code Execution) or access to code of projects they are not a member of. 1. Add the security researcher to the [Security Researcher Acknowledgments list](http://about.gitlab.com/vulnerability-acknowledgements/) 1. Thank the security researcher in an email for their cooperation 1. Update the blog post and the CHANGELOG when we receive the CVE number diff --git a/doc/update/2.6-to-3.0.md b/doc/update/2.6-to-3.0.md index 2044b659468..4827ef9501a 100644 --- a/doc/update/2.6-to-3.0.md +++ b/doc/update/2.6-to-3.0.md @@ -1,4 +1,5 @@ # From 2.6 to 3.0 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/2.6-to-3.0.md) for the most up to date instructions.* ## 1. Stop server & resque diff --git a/doc/update/2.9-to-3.0.md b/doc/update/2.9-to-3.0.md index 8af86b0dc98..f4a997a8c5e 100644 --- a/doc/update/2.9-to-3.0.md +++ b/doc/update/2.9-to-3.0.md @@ -1,4 +1,5 @@ # From 2.9 to 3.0 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/2.9-to-3.0.md) for the most up to date instructions.* ## 1. Stop server & resque diff --git a/doc/update/3.0-to-3.1.md b/doc/update/3.0-to-3.1.md index 3206df3499b..a30485c42f7 100644 --- a/doc/update/3.0-to-3.1.md +++ b/doc/update/3.0-to-3.1.md @@ -1,4 +1,5 @@ # From 3.0 to 3.1 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/3.0-to-3.1.md) for the most up to date instructions.* **IMPORTANT!** diff --git a/doc/update/3.1-to-4.0.md b/doc/update/3.1-to-4.0.md index 165f4e6a308..f1ef4df4744 100644 --- a/doc/update/3.1-to-4.0.md +++ b/doc/update/3.1-to-4.0.md @@ -1,4 +1,5 @@ # From 3.1 to 4.0 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/3.1-to-4.0.md) for the most up to date instructions.* ## Important changes diff --git a/doc/update/4.0-to-4.1.md b/doc/update/4.0-to-4.1.md index 4149ed6b08d..d89d5235917 100644 --- a/doc/update/4.0-to-4.1.md +++ b/doc/update/4.0-to-4.1.md @@ -1,4 +1,5 @@ # From 4.0 to 4.1 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/4.0-to-4.1.md) for the most up to date instructions.* ## Important changes diff --git a/doc/update/4.1-to-4.2.md b/doc/update/4.1-to-4.2.md index 5ee8e8781e9..6fe4412ff90 100644 --- a/doc/update/4.1-to-4.2.md +++ b/doc/update/4.1-to-4.2.md @@ -1,4 +1,5 @@ # From 4.1 to 4.2 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/4.1-to-4.2.md) for the most up to date instructions.* ## 1. Stop server & Resque diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md index 0a929591dec..f9faf65f952 100644 --- a/doc/update/4.2-to-5.0.md +++ b/doc/update/4.2-to-5.0.md @@ -1,4 +1,5 @@ # From 4.2 to 5.0 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/4.2-to-5.0.md) for the most up to date instructions.* ## Warning diff --git a/doc/update/5.0-to-5.1.md b/doc/update/5.0-to-5.1.md index 0e597abb1a9..9fbd1f88515 100644 --- a/doc/update/5.0-to-5.1.md +++ b/doc/update/5.0-to-5.1.md @@ -1,4 +1,5 @@ # From 5.0 to 5.1 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.0-to-5.1.md) for the most up to date instructions.* ## Warning diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md index 6ef559ac9f9..cf9c4e4f770 100644 --- a/doc/update/5.1-to-5.2.md +++ b/doc/update/5.1-to-5.2.md @@ -1,4 +1,5 @@ # From 5.1 to 5.2 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.1-to-5.2.md) for the most up to date instructions.* ## Warning diff --git a/doc/update/5.1-to-5.4.md b/doc/update/5.1-to-5.4.md index 8ec56b266ca..97a98ede070 100644 --- a/doc/update/5.1-to-5.4.md +++ b/doc/update/5.1-to-5.4.md @@ -1,4 +1,5 @@ # From 5.1 to 5.4 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.1-to-5.4.md) for the most up to date instructions.* Also works starting from 5.2. diff --git a/doc/update/5.1-to-6.0.md b/doc/update/5.1-to-6.0.md index ef412b45695..a3fdd92bd2f 100644 --- a/doc/update/5.1-to-6.0.md +++ b/doc/update/5.1-to-6.0.md @@ -1,4 +1,5 @@ # From 5.1 to 6.0 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.1-to-6.0.md) for the most up to date instructions.* ## Warning diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md index 61ddf135641..27613aeda07 100644 --- a/doc/update/5.2-to-5.3.md +++ b/doc/update/5.2-to-5.3.md @@ -1,4 +1,5 @@ # From 5.2 to 5.3 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.2-to-5.3.md) for the most up to date instructions.* ## Warning diff --git a/doc/update/5.3-to-5.4.md b/doc/update/5.3-to-5.4.md index 8a0d43e3e64..577b9a585ff 100644 --- a/doc/update/5.3-to-5.4.md +++ b/doc/update/5.3-to-5.4.md @@ -1,4 +1,5 @@ # From 5.3 to 5.4 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.3-to-5.4.md) for the most up to date instructions.* ## 0. Backup diff --git a/doc/update/5.4-to-6.0.md b/doc/update/5.4-to-6.0.md index ba8f8e39584..d9c6d9bfb91 100644 --- a/doc/update/5.4-to-6.0.md +++ b/doc/update/5.4-to-6.0.md @@ -1,9 +1,13 @@ # From 5.4 to 6.0 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.4-to-6.0.md) for the most up to date instructions.* ## Warning GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. +**You need to follow this guide first, before updating past 6.0, as it contains critical migration steps that are only present +in the `6-0-stable` branch** + ## Deprecations ### Global projects diff --git a/doc/update/6.0-to-6.1.md b/doc/update/6.0-to-6.1.md index 9d67a3bcb96..c5eba1c01c4 100644 --- a/doc/update/6.0-to-6.1.md +++ b/doc/update/6.0-to-6.1.md @@ -1,4 +1,5 @@ # From 6.0 to 6.1 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.0-to-6.1.md) for the most up to date instructions.* ## Warning diff --git a/doc/update/6.1-to-6.2.md b/doc/update/6.1-to-6.2.md index 11b124cf263..a534528108a 100644 --- a/doc/update/6.1-to-6.2.md +++ b/doc/update/6.1-to-6.2.md @@ -1,4 +1,5 @@ # From 6.1 to 6.2 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.1-to-6.2.md) for the most up to date instructions.* **You should update to 6.1 before installing 6.2 so all the necessary conversions are run.** diff --git a/doc/update/6.2-to-6.3.md b/doc/update/6.2-to-6.3.md index e9b3bdd2f54..b08ebde0808 100644 --- a/doc/update/6.2-to-6.3.md +++ b/doc/update/6.2-to-6.3.md @@ -1,4 +1,5 @@ # From 6.2 to 6.3 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.2-to-6.3.md) for the most up to date instructions.* **Requires version: 6.1 or 6.2.** diff --git a/doc/update/6.3-to-6.4.md b/doc/update/6.3-to-6.4.md index 96c2895981d..951d92dfeb5 100644 --- a/doc/update/6.3-to-6.4.md +++ b/doc/update/6.3-to-6.4.md @@ -1,4 +1,5 @@ # From 6.3 to 6.4 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.3-to-6.4.md) for the most up to date instructions.* ## 0. Backup diff --git a/doc/update/6.4-to-6.5.md b/doc/update/6.4-to-6.5.md index 1624296fc3f..0dae9a9fe59 100644 --- a/doc/update/6.4-to-6.5.md +++ b/doc/update/6.4-to-6.5.md @@ -1,4 +1,5 @@ # From 6.4 to 6.5 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.4-to-6.5.md) for the most up to date instructions.* ## 0. Backup diff --git a/doc/update/6.5-to-6.6.md b/doc/update/6.5-to-6.6.md index 544eee17fec..c24e83eb006 100644 --- a/doc/update/6.5-to-6.6.md +++ b/doc/update/6.5-to-6.6.md @@ -1,4 +1,5 @@ # From 6.5 to 6.6 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.5-to-6.6.md) for the most up to date instructions.* ## 0. Backup diff --git a/doc/update/6.6-to-6.7.md b/doc/update/6.6-to-6.7.md index 77ac4d0bfa6..5622a7001ed 100644 --- a/doc/update/6.6-to-6.7.md +++ b/doc/update/6.6-to-6.7.md @@ -1,4 +1,5 @@ # From 6.6 to 6.7 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.6-to-6.7.md) for the most up to date instructions.* ## 0. Backup diff --git a/doc/update/6.7-to-6.8.md b/doc/update/6.7-to-6.8.md index 16f3439c998..4fb90639f16 100644 --- a/doc/update/6.7-to-6.8.md +++ b/doc/update/6.7-to-6.8.md @@ -1,4 +1,5 @@ # From 6.7 to 6.8 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.7-to-6.8.md) for the most up to date instructions.* ## 0. Backup diff --git a/doc/update/6.8-to-6.9.md b/doc/update/6.8-to-6.9.md index 9efb384ff59..b9b8b63f652 100644 --- a/doc/update/6.8-to-6.9.md +++ b/doc/update/6.8-to-6.9.md @@ -1,4 +1,5 @@ # From 6.8 to 6.9 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.8-to-6.9.md) for the most up to date instructions.* ### 0. Backup diff --git a/doc/update/6.9-to-7.0.md b/doc/update/6.9-to-7.0.md index 1f3421a799b..236430b5951 100644 --- a/doc/update/6.9-to-7.0.md +++ b/doc/update/6.9-to-7.0.md @@ -1,4 +1,5 @@ # From 6.9 to 7.0 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.9-to-7.0.md) for the most up to date instructions.* ### 0. Backup diff --git a/doc/update/6.x-or-7.x-to-7.7.md b/doc/update/6.x-or-7.x-to-7.8.md index e9a0d3d4c65..673d9253d62 100644 --- a/doc/update/6.x-or-7.x-to-7.7.md +++ b/doc/update/6.x-or-7.x-to-7.8.md @@ -1,6 +1,7 @@ -# From 6.x or 7.x to 7.7 +# From 6.x or 7.x to 7.8 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.8.md) for the most up to date instructions.* -This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.7. +This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.8. ## Global issue numbers @@ -70,7 +71,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut For GitLab Community Edition: ```bash -sudo -u git -H git checkout 7-7-stable +sudo -u git -H git checkout 7-8-stable ``` OR @@ -78,7 +79,7 @@ OR For GitLab Enterprise Edition: ```bash -sudo -u git -H git checkout 7-7-stable-ee +sudo -u git -H git checkout 7-8-stable-ee ``` ## 4. Install additional packages @@ -122,7 +123,7 @@ sudo apt-get install libkrb5-dev ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.4.1 +sudo -u git -H git checkout v2.5.4 ``` ## 7. Install libs, migrations, etc. @@ -157,14 +158,12 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab TIP: to see what changed in `gitlab.yml.example` in this release use next command: ``` -git diff 6-0-stable:config/gitlab.yml.example 7-7-stable:config/gitlab.yml.example +git diff 6-0-stable:config/gitlab.yml.example 7-8-stable:config/gitlab.yml.example ``` -* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/config/gitlab.yml.example but with your settings. -* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/config/unicorn.rb.example but with your settings. -* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.4.0/config.yml.example but with your settings. -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/lib/support/nginx/gitlab but with your settings. -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/lib/support/nginx/gitlab-ssl but with your settings. +* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/config/gitlab.yml.example but with your settings. +* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/config/unicorn.rb.example but with your settings. +* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.5.4/config.yml.example but with your settings. * Copy rack attack middleware config ```bash @@ -177,6 +176,12 @@ sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab ``` +### Change Nginx settings + +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/lib/support/nginx/gitlab but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/lib/support/nginx/gitlab-ssl but with your settings. +* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section. + ## 9. Start application sudo service gitlab start @@ -199,7 +204,7 @@ If all items are green, then congratulations upgrade complete! When using Google omniauth login, changes of the Google account required. Ensure that `Contacts API` and the `Google+ API` are enabled in the [Google Developers Console](https://console.developers.google.com/). -More details can be found at the [integration documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/google.md). +More details can be found at the [integration documentation](../../../master/doc/integration/google.md). ## 12. Optional optimizations for GitLab setups with MySQL databases @@ -234,7 +239,7 @@ SET foreign_key_checks = 1; # Find MySQL users mysql> SELECT user FROM mysql.user WHERE user LIKE '%git%'; -# If git user exists and gitlab user does not exist +# If git user exists and gitlab user does not exist # you are done with the database cleanup tasks mysql> \q @@ -272,11 +277,11 @@ mysql> \q sudo -u git -H editor /home/git/gitlab/config/database.yml ``` -## Things went south? Revert to previous version (6.0) +## Things went south? Revert to previous version (7.0) ### 1. Revert the code to the previous version -Follow the [upgrade guide from 5.4 to 6.0](5.4-to-6.0.md), except for the database migration (the backup is already migrated to the previous version). +Follow the [upgrade guide from 6.9 to 7.0](6.9-to-7.0.md), except for the database migration (the backup is already migrated to the previous version). ### 2. Restore from the backup: diff --git a/doc/update/7.0-to-7.1.md b/doc/update/7.0-to-7.1.md index 82bb5708734..a4e9be9946e 100644 --- a/doc/update/7.0-to-7.1.md +++ b/doc/update/7.0-to-7.1.md @@ -1,4 +1,5 @@ # From 7.0 to 7.1 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.0-to-7.1.md) for the most up to date instructions.* ### 0. Backup diff --git a/doc/update/7.1-to-7.2.md b/doc/update/7.1-to-7.2.md index 699111f0143..88cb63d7d41 100644 --- a/doc/update/7.1-to-7.2.md +++ b/doc/update/7.1-to-7.2.md @@ -1,4 +1,5 @@ # From 7.1 to 7.2 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.1-to-7.2.md) for the most up to date instructions.* ## Editable labels diff --git a/doc/update/7.2-to-7.3.md b/doc/update/7.2-to-7.3.md index ebdd4ff60fa..18f77d6396e 100644 --- a/doc/update/7.2-to-7.3.md +++ b/doc/update/7.2-to-7.3.md @@ -1,4 +1,5 @@ # From 7.2 to 7.3 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.2-to-7.3.md) for the most up to date instructions.* ### 0. Backup diff --git a/doc/update/7.3-to-7.4.md b/doc/update/7.3-to-7.4.md index 085cb80a97f..53e739c06fb 100644 --- a/doc/update/7.3-to-7.4.md +++ b/doc/update/7.3-to-7.4.md @@ -1,4 +1,5 @@ # From 7.3 to 7.4 +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.3-to-7.4.md) for the most up to date instructions.* ### 0. Stop server diff --git a/doc/update/7.7-to-7.8.md b/doc/update/7.7-to-7.8.md new file mode 100644 index 00000000000..46ca163c1bb --- /dev/null +++ b/doc/update/7.7-to-7.8.md @@ -0,0 +1,120 @@ +# From 7.7 to 7.8 + +### 0. Stop server + + sudo service gitlab stop + +### 1. Backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 2. Get latest code + +```bash +sudo -u git -H git fetch --all +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +``` + +For GitLab Community Edition: + +```bash +sudo -u git -H git checkout 7-8-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +sudo -u git -H git checkout 7-8-stable-ee +``` + +### 3. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell +sudo -u git -H git fetch +sudo -u git -H git checkout v2.5.4 +``` + +### 4. Install libs, migrations, etc. + +```bash +sudo apt-get install libkrb5-dev + +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without ... postgres') +sudo -u git -H bundle install --without development test postgres --deployment + +# PostgreSQL installations (note: the line below states '--without ... mysql') +sudo -u git -H bundle install --without development test mysql --deployment + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production + +# Update init.d script +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +``` + +### 5. Update config files + +#### New configuration options for `gitlab.yml` + +There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. + +``` +git diff origin/7-7-stable:config/gitlab.yml.example origin/7-8-stable:config/gitlab.yml.example +``` + +#### Change Nginx settings + +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your settings. +* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section. + +#### Setup time zone (optional) + +Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`](config/application.rb) (unlikely), unset it. + +### 6. Start application + + sudo service gitlab start + sudo service nginx restart + +### 7. Check application status + +Check if GitLab and its environment are configured correctly: + + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production + +To make sure you didn't miss anything run a more thorough check with: + + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production + +If all items are green, then congratulations upgrade is complete! + +### 8. GitHub settings (if applicable) + +If you are using GitHub as an OAuth provider for authentication, you should change the callback URL so that it +only contains a root URL (ex. `https://gitlab.example.com/`) + +## Things went south? Revert to previous version (7.7) + +### 1. Revert the code to the previous version +Follow the [upgrade guide from 7.6 to 7.7](7.6-to-7.7.md), except for the database migration +(The backup is already migrated to the previous version) + +### 2. Restore from the backup: + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` +If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. diff --git a/doc/update/README.md b/doc/update/README.md index 5380ddbd030..0472537eeb5 100644 --- a/doc/update/README.md +++ b/doc/update/README.md @@ -4,11 +4,11 @@ Depending on the installation method and your GitLab version, there are multiple - [Omnibus update guide](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md) contains the steps needed to update a GitLab [package](https://about.gitlab.com/downloads/). -## Manual Installation +## Installation from source -- [The individual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update) are for those who have installed GitLab manually. +- [The individual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update) are for those who have installed GitLab from source. - [The CE to EE update guides](https://gitlab.com/subscribers/gitlab-ee/tree/master/doc/update) are for subscribers of the Enterprise Edition only. The steps are very similar to a version upgrade: stop the server, get the code, update config files for the new functionality, install libs and do migrations, update the init script, start the application and check the application status. -- [Upgrader](upgrader.md) is an automatic ruby script that performs the update for manual installations. +- [Upgrader](upgrader.md) is an automatic ruby script that performs the update for installations from source. - [Patch versions](patch_versions.md) guide includes the steps needed for a patch version, eg. 6.2.0 to 6.2.1. ## Miscellaneous diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 229689392b8..50941db25f6 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -1,4 +1,5 @@ # Migrating GitLab from MySQL to Postgres +*Make sure you view this [guide from the `master` branch](../../../master/doc/update/mysql_to_postgresql.md) for the most up to date instructions.* If you are replacing MySQL with Postgres while keeping GitLab on the same server all you need to do is to export from MySQL, import into Postgres and rebuild the indexes as described below. If you are also moving GitLab to another server, or if you are switching to omnibus-gitlab, you may want to use a GitLab backup file. The second part of this documents explains the procedure to do this. @@ -11,7 +12,7 @@ sudo service gitlab stop # Update /home/git/gitlab/config/database.yml -git clone https://github.com/gitlabhq/mysql-postgresql-converter.git +git clone https://github.com/gitlabhq/mysql-postgresql-converter.git -b gitlab cd mysql-postgresql-converter mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production -p python db_converter.py databasename.mysql databasename.psql @@ -38,7 +39,7 @@ On non-omnibus installations (distributed using Git) we retrieve the index decla ``` # Clone the database converter on your Postgres-backed GitLab server cd /tmp -git clone https://github.com/gitlabhq/mysql-postgresql-converter.git +git clone https://github.com/gitlabhq/mysql-postgresql-converter.git -b gitlab cd /home/git/gitlab @@ -59,7 +60,7 @@ On omnibus-gitlab we need to get the index declarations from a file called `sche ``` # Clone the database converter on your Postgres-backed GitLab server cd /tmp -/opt/gitlab/embedded/bin/git clone https://github.com/gitlabhq/mysql-postgresql-converter.git +/opt/gitlab/embedded/bin/git clone https://github.com/gitlabhq/mysql-postgresql-converter.git -b gitlab cd /tmp/mysql-postgresql-converter # Download schema.rb.bundled if necessary @@ -75,7 +76,7 @@ test -e /opt/gitlab/embedded/service/gitlab-rails/db/schema.rb.bundled || sudo / ## Converting a GitLab backup file from MySQL to Postgres **Note:** Please make sure to have Python 2.7.x (or higher) installed. -GitLab backup files (<timestamp>_gitlab_backup.tar) contain a SQL dump. Using the lanyrd database converter we can replace a MySQL database dump inside the tar file with a Postgres database dump. This can be useful if you are moving to another server. +GitLab backup files (`<timestamp>_gitlab_backup.tar`) contain a SQL dump. Using the lanyrd database converter we can replace a MySQL database dump inside the tar file with a Postgres database dump. This can be useful if you are moving to another server. ``` # Stop GitLab @@ -97,7 +98,7 @@ cd tmp/backups/postgresql sudo -u git -H mysqldump --compatible=postgresql --default-character-set=utf8 -r gitlabhq_production.mysql -u root gitlabhq_production -p # Clone the database converter -sudo -u git -H git clone https://github.com/gitlabhq/mysql-postgresql-converter.git +sudo -u git -H git clone https://github.com/gitlabhq/mysql-postgresql-converter.git -b gitlab # Convert gitlabhq_production.mysql sudo -u git -H mkdir db diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 629c46ad030..ad302492556 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -1,4 +1,5 @@ # Universal update guide for patch versions +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/patch_versions.md) for the most up to date instructions.* For example from 6.2.0 to 6.2.1, also see the [semantic versioning specification](http://semver.org/). diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md index 5016ee4baad..4ed35b2b562 100644 --- a/doc/update/upgrader.md +++ b/doc/update/upgrader.md @@ -1,4 +1,5 @@ # GitLab Upgrader +*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/upgrader.md) for the most up to date instructions.* GitLab Upgrader - a ruby script that allows you easily upgrade GitLab to latest minor version. diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index e3399e5f1b8..3cccd84b063 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -21,19 +21,23 @@ Triggered when you push to the repository except when pushing tags. "ref": "refs/heads/master", "user_id": 4, "user_name": "John Smith", + "user_email": "john@example.com", "project_id": 15, "repository": { "name": "Diaspora", - "url": "git@example.com:diaspora.git", + "url": "git@example.com:mike/diasporadiaspora.git", "description": "", - "homepage": "http://example.com/diaspora" + "homepage": "http://example.com/mike/diaspora", + "git_http_url":"http://example.com/mike/diaspora.git", + "git_ssh_url":"git@example.com:mike/diaspora.git", + "visibility_level":0 }, "commits": [ { "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "message": "Update Catalan translation to e38cb41.", "timestamp": "2011-12-12T14:27:31+02:00", - "url": "http://example.com/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "author": { "name": "Jordi Mallach", "email": "jordi@softcatala.org" @@ -43,7 +47,7 @@ Triggered when you push to the repository except when pushing tags. "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "message": "fixed readme", "timestamp": "2012-01-03T23:36:29+02:00", - "url": "http://example.com/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "author": { "name": "GitLab dev user", "email": "gitlabdev@dv6700.(none)" @@ -72,8 +76,13 @@ Triggered when you create (or delete) tags to the repository. "name": "jsmith", "url": "ssh://git@example.com/jsmith/example.git", "description": "", - "homepage": "http://example.com/jsmith/example" - } + "homepage": "http://example.com/jsmith/example", + "git_http_url":"http://example.com/jsmith/example.git", + "git_ssh_url":"git@example.com:jsmith/example.git", + "visibility_level":0 + }, + "commits": [], + "total_commits_count": 0 } ``` diff --git a/doc/workflow/README.md b/doc/workflow/README.md index 3c0007d8198..6e70235f5b8 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -9,5 +9,6 @@ - [Notifications](notifications.md) - [Migrating from SVN to GitLab](migrating_from_svn.md) - [Project importing from GitHub to GitLab](import_projects_from_github.md) +- [Project importing from GitLab.com to your private GitLab instance](import_projects_from_gitlab_com.md) - [Protected branches](protected_branches.md) - [Web Editor](web_editor.md) diff --git a/doc/workflow/gitlab_importer/importer.png b/doc/workflow/gitlab_importer/importer.png Binary files differnew file mode 100644 index 00000000000..d2a286d8cac --- /dev/null +++ b/doc/workflow/gitlab_importer/importer.png diff --git a/doc/workflow/gitlab_importer/new_project_page.png b/doc/workflow/gitlab_importer/new_project_page.png Binary files differnew file mode 100644 index 00000000000..5e239208e1e --- /dev/null +++ b/doc/workflow/gitlab_importer/new_project_page.png diff --git a/doc/workflow/import_projects_from_gitlab_com.md b/doc/workflow/import_projects_from_gitlab_com.md new file mode 100644 index 00000000000..f4c4e955d46 --- /dev/null +++ b/doc/workflow/import_projects_from_gitlab_com.md @@ -0,0 +1,18 @@ +# Project importing from GitLab.com to your private GitLab instance + +You can import your existing GitLab.com projects to your GitLab instance. But keep in mind that it is possible only if +GitLab support is enabled on your GitLab instance. +You can read more about Gitlab support [here](http://doc.gitlab.com/ce/integration/gitlab.html) +To get to the importer page you need to go to "New project" page. + + + +Click on the "Import projects from Gitlab.com" link and you will be redirected to GitLab.com +for permission to access your projects. After accepting, you'll be automatically redirected to the importer. + + + + + +To import a project, you can simple click "Import". The importer will import your repository and issues. +Once the importer is done, a new GitLab project will be created with your imported data.
\ No newline at end of file diff --git a/doc/workflow/web_editor.md b/doc/workflow/web_editor.md index bcadf5e8c0d..7fc8f96b9ec 100644 --- a/doc/workflow/web_editor.md +++ b/doc/workflow/web_editor.md @@ -10,7 +10,7 @@ to create the first file and open it in the web editor.  -Fill in a file name, some content, a commit message and press the commit button. +Fill in a file name, some content, a commit message, branch name and press the commit button. The file will be saved to the repository.  @@ -21,6 +21,6 @@ viewing the file.  Editing a file is almost the same as creating a new file, -with as addition the ability to preview your changes in a separate tab. +with as addition the ability to preview your changes in a separate tab. Also you can save your change to another branch by filling out field `branch`  diff --git a/doc/workflow/web_editor/edit_file.png b/doc/workflow/web_editor/edit_file.png Binary files differindex 1522c50b62f..f480c69ac3e 100644 --- a/doc/workflow/web_editor/edit_file.png +++ b/doc/workflow/web_editor/edit_file.png diff --git a/doc/workflow/web_editor/new_file.png b/doc/workflow/web_editor/new_file.png Binary files differindex 80941f37cea..55ebd9e0257 100644 --- a/doc/workflow/web_editor/new_file.png +++ b/doc/workflow/web_editor/new_file.png diff --git a/docker/.dockerignore b/docker/.dockerignore new file mode 100644 index 00000000000..dd449725e18 --- /dev/null +++ b/docker/.dockerignore @@ -0,0 +1 @@ +*.md diff --git a/docker/Dockerfile b/docker/Dockerfile index ec0923bd4c7..4eb280f9554 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update -q \ # If the Omnibus package version below is outdated please contribute a merge request to update it. # If you run GitLab Enterprise Edition point it to a location where you have downloaded it. RUN TMP_FILE=$(mktemp); \ - wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.7.2-omnibus.5.4.2.ci-1_amd64.deb \ + wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.8.3-omnibus-1_amd64.deb \ && dpkg -i $TMP_FILE \ && rm -f $TMP_FILE @@ -26,9 +26,12 @@ RUN mkdir -p /opt/gitlab/sv/sshd/supervise \ # Expose web & ssh EXPOSE 80 22 -# Volume & configuration +# Declare volumes VOLUME ["/var/opt/gitlab", "/var/log/gitlab", "/etc/gitlab"] -ADD gitlab.rb /etc/gitlab/ -# Default is to run runit & reconfigure -CMD gitlab-ctl reconfigure & /opt/gitlab/embedded/bin/runsvdir-start +# Copy assets +COPY assets/gitlab.rb /etc/gitlab/ +COPY assets/wrapper /usr/local/bin/ + +# Wrapper to handle signal, trigger runit and reconfigure GitLab +CMD ["/usr/local/bin/wrapper"] diff --git a/docker/gitlab.rb b/docker/assets/gitlab.rb index 7fddf309c01..7fddf309c01 100644 --- a/docker/gitlab.rb +++ b/docker/assets/gitlab.rb diff --git a/docker/assets/wrapper b/docker/assets/wrapper new file mode 100755 index 00000000000..9e6e7a05903 --- /dev/null +++ b/docker/assets/wrapper @@ -0,0 +1,17 @@ +#!/bin/bash + +function sigterm_handler() { + echo "SIGTERM signal received, try to gracefully shutdown all services..." + gitlab-ctl stop +} + +trap "sigterm_handler; exit" TERM + +function entrypoint() { + # Default is to run runit and reconfigure GitLab + gitlab-ctl reconfigure & + /opt/gitlab/embedded/bin/runsvdir-start & + wait +} + +entrypoint diff --git a/features/dashboard/archived_projects.feature b/features/dashboard/archived_projects.feature index 3af93bc373c..69b3a776441 100644 --- a/features/dashboard/archived_projects.feature +++ b/features/dashboard/archived_projects.feature @@ -10,8 +10,3 @@ Feature: Dashboard Archived Projects Scenario: I should see non-archived projects on dashboard Then I should see "Shop" project link And I should not see "Forum" project link - - Scenario: I should see all projects on projects page - And I visit dashboard projects page - Then I should see "Shop" project link - And I should see "Forum" project link diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature index bebaa78e46c..1959d327082 100644 --- a/features/dashboard/dashboard.feature +++ b/features/dashboard/dashboard.feature @@ -27,11 +27,11 @@ Feature: Dashboard Scenario: I should see User joined Project event Given user with name "John Doe" joined project "Shop" When I visit dashboard page - Then I should see "John Doe joined project at Shop" event + Then I should see "John Doe joined project Shop" event @javascript Scenario: I should see User left Project event Given user with name "John Doe" joined project "Shop" And user with name "John Doe" left project "Shop" When I visit dashboard page - Then I should see "John Doe left project at Shop" event + Then I should see "John Doe left project Shop" event diff --git a/features/profile/group.feature b/features/dashboard/group.feature index e2fbfde77be..cf4b8d7283b 100644 --- a/features/profile/group.feature +++ b/features/dashboard/group.feature @@ -1,5 +1,5 @@ -@profile -Feature: Profile Group +@dashboard +Feature: Dashboard Group Background: Given I sign in as "John Doe" And "John Doe" is owner of group "Owned" @@ -10,18 +10,18 @@ Feature: Profile Group @javascript Scenario: Owner should be able to leave from group if he is not the last owner Given "Mary Jane" is owner of group "Owned" - When I visit profile groups page + When I visit dashboard groups page Then I should see group "Owned" in group list Then I should see group "Guest" in group list When I click on the "Leave" button for group "Owned" - And I visit profile groups page + And I visit dashboard groups page Then I should not see group "Owned" in group list Then I should see group "Guest" in group list @javascript Scenario: Owner should not be able to leave from group if he is the last owner Given "Mary Jane" is guest of group "Owned" - When I visit profile groups page + When I visit dashboard groups page Then I should see group "Owned" in group list Then I should see group "Guest" in group list Then I should not see the "Leave" button for group "Owned" @@ -29,20 +29,28 @@ Feature: Profile Group @javascript Scenario: Guest should be able to leave from group Given "Mary Jane" is guest of group "Guest" - When I visit profile groups page + When I visit dashboard groups page Then I should see group "Owned" in group list Then I should see group "Guest" in group list When I click on the "Leave" button for group "Guest" - When I visit profile groups page + When I visit dashboard groups page Then I should see group "Owned" in group list Then I should not see group "Guest" in group list @javascript Scenario: Guest should be able to leave from group even if he is the only user in the group - When I visit profile groups page + When I visit dashboard groups page Then I should see group "Owned" in group list Then I should see group "Guest" in group list When I click on the "Leave" button for group "Guest" - When I visit profile groups page + When I visit dashboard groups page Then I should see group "Owned" in group list Then I should not see group "Guest" in group list + + Scenario: Create a group from dasboard + And I visit dashboard groups page + And I click new group link + And submit form with new group "Samurai" info + Then I should be redirected to group "Samurai" page + And I should see newly created group "Samurai" + diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature deleted file mode 100644 index bb4e84f0159..00000000000 --- a/features/dashboard/projects.feature +++ /dev/null @@ -1,9 +0,0 @@ -@dashboard -Feature: Dashboard Projects - Background: - Given I sign in as a user - And I own project "Shop" - And I visit dashboard projects page - - Scenario: I should see projects list - Then I should see projects list diff --git a/features/dashboard/starred_projects.feature b/features/dashboard/starred_projects.feature new file mode 100644 index 00000000000..9dfd2fbab9c --- /dev/null +++ b/features/dashboard/starred_projects.feature @@ -0,0 +1,12 @@ +@dashboard +Feature: Dashboard Starred Projects + Background: + Given I sign in as a user + And public project "Community" + And I starred project "Community" + And I own project "Shop" + And I visit dashboard starred projects page + + Scenario: I should see projects list + Then I should see project "Community" + And I should not see project "Shop" diff --git a/features/groups.feature b/features/groups.feature index b5ff03db844..05546e0d6ef 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -10,14 +10,6 @@ Feature: Groups Then I should see group "Owned" projects list And I should see projects activity feed - Scenario: Create a group from dasboard - When I visit group "Owned" page - And I visit dashboard page - And I click new group link - And submit form with new group "Samurai" info - Then I should be redirected to group "Samurai" page - And I should see newly created group "Samurai" - Scenario: I should see group "Owned" issues list Given project from group "Owned" has issues assigned to me When I visit group "Owned" issues page diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature index ed548177837..05faad4e645 100644 --- a/features/project/active_tab.feature +++ b/features/project/active_tab.feature @@ -106,24 +106,19 @@ Feature: Project Active Tab And no other sub tabs should be active And the active main tab should be Commits - # Sub Tabs: Issues - Scenario: On Project Issues/Browse Given I visit my project's issues page - Then the active sub tab should be Issues - And no other sub tabs should be active - And the active main tab should be Issues + Then the active main tab should be Issues + And no other main tabs should be active Scenario: On Project Issues/Milestones Given I visit my project's issues page And I click the "Milestones" tab - Then the active sub tab should be Milestones - And no other sub tabs should be active - And the active main tab should be Issues + Then the active main tab should be Milestones + And no other main tabs should be active Scenario: On Project Issues/Labels Given I visit my project's issues page And I click the "Labels" tab - Then the active sub tab should be Labels - And no other sub tabs should be active - And the active main tab should be Issues + Then the active main tab should be Labels + And no other main tabs should be active diff --git a/features/project/archived.feature b/features/project/archived.feature index 9aac29384ba..ad466f4f307 100644 --- a/features/project/archived.feature +++ b/features/project/archived.feature @@ -14,15 +14,6 @@ Feature: Project Archived And I visit project "Forum" page Then I should see "Archived" - Scenario: I should not see archived on projects page with no archived projects - And I visit dashboard projects page - Then I should not see "Archived" - - Scenario: I should see archived on projects page with archived projects - And project "Forum" is archived - And I visit dashboard projects page - Then I should see "Archived" - Scenario: I archive project When project "Shop" has push event And I visit project "Shop" page diff --git a/features/project/commits/comments.feature b/features/project/commits/comments.feature index afcf0fdbb07..c41075d7ad4 100644 --- a/features/project/commits/comments.feature +++ b/features/project/commits/comments.feature @@ -41,3 +41,9 @@ Feature: Project Commits Comments Given I leave a comment like "XML attached" And I delete a comment Then I should not see a comment saying "XML attached" + + @javascript + Scenario: I can edit a comment with +1 + Given I leave a comment like "XML attached" + And I edit the last comment with a +1 + Then I should see +1 in the description diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature index 28ea44530fe..283979204db 100644 --- a/features/project/issues/issues.feature +++ b/features/project/issues/issues.feature @@ -139,6 +139,15 @@ Feature: Project Issues And I leave a comment with task markdown Then I should not see task checkboxes in the comment + @javascript + Scenario: Issue notes should be editable with +1 + Given project "Shop" has "Tasks-open" open issue with task markdown + When I visit issue page "Tasks-open" + And I leave a comment with a header containing "Comment with a header" + Then The comment with the header should not have an ID + And I edit the last comment with a +1 + Then I should see +1 in the description + # Task status in issues list Scenario: Issues list should display task status diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 7c029f05d75..adad100e56c 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -218,3 +218,10 @@ Feature: Project Merge Requests And I click link "Edit" for the merge request And I preview a description text like "Bug fixed :smile:" Then I should see the Markdown write tab + + @javascript + Scenario: I search merge request + Given I click link "All" + When I fill in merge request search with "Fe" + Then I should see "Feature NS-03" in merge requests + And I should not see "Bug NS-04" in merge requests diff --git a/features/project/service.feature b/features/project/service.feature index d0600aca010..fdff640ec85 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -61,6 +61,12 @@ Feature: Project Services And I fill email on push settings Then I should see email on push service settings saved + Scenario: Activate Irker (IRC Gateway) service + When I visit project "Shop" services page + And I click Irker service link + And I fill Irker settings + Then I should see Irker service settings saved + Scenario: Activate Atlassian Bamboo CI service When I visit project "Shop" services page And I click Atlassian Bamboo CI service link diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index ee8d0bffa9b..90b966dd645 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -34,6 +34,17 @@ Feature: Project Source Browse Files Then I am redirected to the new file And I should see its new content + @javascript + Scenario: I can create and commit file and specify new branch + Given I click on "new file" link in repo + And I edit code + And I fill the new file name + And I fill the commit message + And I fill the new branch name + And I click on "Commit Changes" + Then I am redirected to the new file on new branch + And I should see its new content + @javascript @tricky Scenario: I can create file in empty repo Given I own an empty project @@ -83,6 +94,17 @@ Feature: Project Source Browse Files Then I am redirected to the ".gitignore" And I should see its new content + @javascript + Scenario: I can edit and commit file to new branch + Given I click on ".gitignore" file in repo + And I click button "Edit" + And I edit code + And I fill the commit message + And I fill the new branch name + And I click on "Commit Changes" + Then I am redirected to the ".gitignore" on new branch + And I should see its new content + @javascript @wip Scenario: If I don't change the content of the file I see an error message Given I click on ".gitignore" file in repo diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb index 2fd6385fe7b..9be4d39d2d5 100644 --- a/features/steps/admin/projects.rb +++ b/features/steps/admin/projects.rb @@ -15,17 +15,17 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps step 'I should see project details' do project = Project.first - current_path.should == admin_project_path(project) + current_path.should == admin_namespace_project_path(project.namespace, project) page.should have_content(project.name_with_namespace) page.should have_content(project.creator.name) end step 'I visit admin project page' do - visit admin_project_path(project) + visit admin_namespace_project_path(project.namespace, project) end step 'I transfer project to group \'Web\'' do - find(:xpath, "//input[@id='namespace_id']").set group.id + find(:xpath, "//input[@id='new_namespace_id']").set group.id click_button 'Transfer' end diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index 1826ead1d51..8508b2a8096 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -21,7 +21,7 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps end step 'I see prefilled new Merge Request page' do - current_path.should == new_project_merge_request_path(@project) + current_path.should == new_namespace_project_merge_request_path(@project.namespace, @project) find("#merge_request_target_project_id").value.should == @project.id.to_s find("#merge_request_source_branch").value.should == "fix" find("#merge_request_target_branch").value.should == "master" @@ -37,8 +37,8 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps ) end - step 'I should see "John Doe joined project at Shop" event' do - page.should have_content "John Doe joined project at #{project.name_with_namespace}" + step 'I should see "John Doe joined project Shop" event' do + page.should have_content "John Doe joined project #{project.name_with_namespace}" end step 'user with name "John Doe" left project "Shop"' do @@ -50,8 +50,8 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps ) end - step 'I should see "John Doe left project at Shop" event' do - page.should have_content "John Doe left project at #{project.name_with_namespace}" + step 'I should see "John Doe left project Shop" event' do + page.should have_content "John Doe left project #{project.name_with_namespace}" end step 'I have group with projects' do diff --git a/features/steps/profile/group.rb b/features/steps/dashboard/group.rb index 0a10e04e219..8384df2fb59 100644 --- a/features/steps/profile/group.rb +++ b/features/steps/dashboard/group.rb @@ -1,4 +1,4 @@ -class Spinach::Features::ProfileGroup < Spinach::FeatureSteps +class Spinach::Features::DashboardGroup < Spinach::FeatureSteps include SharedAuthentication include SharedGroup include SharedPaths @@ -41,4 +41,23 @@ class Spinach::Features::ProfileGroup < Spinach::FeatureSteps step 'I should not see group "Guest" in group list' do page.should_not have_content("Guest") end + + step 'I click new group link' do + click_link "New Group" + end + + step 'submit form with new group "Samurai" info' do + fill_in 'group_path', with: 'Samurai' + fill_in 'group_description', with: 'Tokugawa Shogunate' + click_button "Create group" + end + + step 'I should be redirected to group "Samurai" page' do + current_path.should == group_path(Group.find_by(name: 'Samurai')) + end + + step 'I should see newly created group "Samurai"' do + page.should have_content "Samurai" + page.should have_content "Tokugawa Shogunate" + end end diff --git a/features/steps/dashboard/projects.rb b/features/steps/dashboard/projects.rb deleted file mode 100644 index 2a348163060..00000000000 --- a/features/steps/dashboard/projects.rb +++ /dev/null @@ -1,11 +0,0 @@ -class Spinach::Features::DashboardProjects < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedProject - - step 'I should see projects list' do - @user.authorized_projects.all.each do |project| - page.should have_link project.name_with_namespace - end - end -end diff --git a/features/steps/dashboard/starred_projects.rb b/features/steps/dashboard/starred_projects.rb new file mode 100644 index 00000000000..b9ad2f13e29 --- /dev/null +++ b/features/steps/dashboard/starred_projects.rb @@ -0,0 +1,15 @@ +class Spinach::Features::DashboardStarredProjects < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + + step 'I starred project "Community"' do + current_user.toggle_star(Project.find_by(name: 'Community')) + end + + step 'I should not see project "Shop"' do + within 'aside' do + page.should_not have_content('Shop') + end + end +end diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb index 8172f7922cc..26b71406bd8 100644 --- a/features/steps/explore/projects.rb +++ b/features/steps/explore/projects.rb @@ -65,7 +65,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps title: "New feature", project: public_project ) - visit project_issues_path(public_project) + visit namespace_project_issues_path(public_project.namespace, public_project) end @@ -84,7 +84,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps title: "New internal feature", project: internal_project ) - visit project_issues_path(internal_project) + visit namespace_project_issues_path(internal_project.namespace, internal_project) end @@ -95,7 +95,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps end step 'I visit "Community" merge requests page' do - visit project_merge_requests_path(public_project) + visit namespace_project_merge_requests_path(public_project.namespace, public_project) end step 'project "Community" has "Bug fix" open merge request' do @@ -112,7 +112,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps end step 'I visit "Internal" merge requests page' do - visit project_merge_requests_path(internal_project) + visit namespace_project_merge_requests_path(internal_project.namespace, internal_project) end step 'project "Internal" has "Feature implemented" open merge request' do diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 610e7fd3a48..91921f5e21c 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -29,7 +29,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I select user "Mary Jane" from list with role "Reporter"' do user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") - click_link 'Add members' + click_button 'Add members' within ".users-group-form" do select2(user.id, from: "#user_ids", multiple: true) select "Reporter", from: "access_level" @@ -72,25 +72,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps author: current_user end - When 'I click new group link' do - click_link "New group" - end - - step 'submit form with new group "Samurai" info' do - fill_in 'group_path', with: 'Samurai' - fill_in 'group_description', with: 'Tokugawa Shogunate' - click_button "Create group" - end - - step 'I should be redirected to group "Samurai" page' do - current_path.should == group_path(Group.find_by(name: 'Samurai')) - end - - step 'I should see newly created group "Samurai"' do - page.should have_content "Samurai" - page.should have_content "Tokugawa Shogunate" - end - step 'I change group "Owned" name to "new-name"' do fill_in 'group_name', with: 'new-name' fill_in 'group_path', with: 'new-name' @@ -110,7 +91,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see new group "Owned" avatar' do - Group.find_by(name: "Owned").avatar.should be_instance_of AttachmentUploader + Group.find_by(name: "Owned").avatar.should be_instance_of AvatarUploader Group.find_by(name: "Owned").avatar.url.should == "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/gitlab_logo.png" end @@ -194,8 +175,8 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I should see group milestone with all issues and MRs assigned to that milestone' do page.should have_content('Milestone GL-113') page.should have_content('Progress: 0 closed – 4 open') - page.should have_link(@issue1.title, href: project_issue_path(@project1, @issue1)) - page.should have_link(@mr3.title, href: project_merge_request_path(@project3, @mr3)) + page.should have_link(@issue1.title, href: namespace_project_issue_path(@project1.namespace, @project1, @issue1)) + page.should have_link(@mr3.title, href: namespace_project_merge_request_path(@project3.namespace, @project3, @mr3)) end protected diff --git a/features/steps/profile/notifications.rb b/features/steps/profile/notifications.rb index df96dddd06e..13e93618eb7 100644 --- a/features/steps/profile/notifications.rb +++ b/features/steps/profile/notifications.rb @@ -7,6 +7,6 @@ class Spinach::Features::ProfileNotifications < Spinach::FeatureSteps end step 'I should see global notifications settings' do - page.should have_content "Notifications settings" + page.should have_content "Notifications Settings" end end diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index a907b0b7dcf..bfbfe7af199 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -3,7 +3,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps include SharedPaths step 'I should see my profile info' do - page.should have_content "Profile settings" + page.should have_content "Profile Settings" end step 'I change my profile info' do @@ -29,7 +29,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps end step 'I should see new avatar' do - @user.avatar.should be_instance_of AttachmentUploader + @user.avatar.should be_instance_of AvatarUploader @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png" end diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb index bb42d15eae5..dd3215adb1a 100644 --- a/features/steps/project/active_tab.rb +++ b/features/steps/project/active_tab.rb @@ -93,11 +93,11 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ensure_active_sub_tab('Issues') end - step 'the active sub tab should be Milestones' do - ensure_active_sub_tab('Milestones') + step 'the active main tab should be Milestones' do + ensure_active_main_tab('Milestones') end - step 'the active sub tab should be Labels' do - ensure_active_sub_tab('Labels') + step 'the active main tab should be Labels' do + ensure_active_main_tab('Labels') end end diff --git a/features/steps/project/archived.rb b/features/steps/project/archived.rb index afbf4d5950d..37ad0c77655 100644 --- a/features/steps/project/archived.rb +++ b/features/steps/project/archived.rb @@ -15,7 +15,7 @@ class Spinach::Features::ProjectArchived < Spinach::FeatureSteps When 'I visit project "Forum" page' do project = Project.find_by(name: "Forum") - visit project_path(project) + visit namespace_project_path(project.namespace, project) end step 'I should not see "Archived"' do diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index d515ee1ac11..b2dccf868b0 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -24,7 +24,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps end step 'I click on commit link' do - visit project_commit_path(@project, sample_commit.id) + visit namespace_project_commit_path(@project.namespace, @project, sample_commit.id) end step 'I see commit info' do @@ -58,7 +58,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps step 'I visit big commit page' do Commit::DIFF_SAFE_FILES = 20 - visit project_commit_path(@project, sample_big_commit.id) + visit namespace_project_commit_path(@project.namespace, @project, sample_big_commit.id) end step 'I see big commit warning' do @@ -68,7 +68,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps end step 'I visit a commit with an image that changed' do - visit project_commit_path(@project, sample_image_commit.id) + visit namespace_project_commit_path(@project.namespace, @project, sample_image_commit.id) end step 'The diff links to both the previous and current image' do diff --git a/features/steps/project/commits/user_lookup.rb b/features/steps/project/commits/user_lookup.rb index 0622fef43bb..63ff84c82ef 100644 --- a/features/steps/project/commits/user_lookup.rb +++ b/features/steps/project/commits/user_lookup.rb @@ -4,11 +4,11 @@ class Spinach::Features::ProjectCommitsUserLookup < Spinach::FeatureSteps include SharedPaths step 'I click on commit link' do - visit project_commit_path(@project, sample_commit.id) + visit namespace_project_commit_path(@project.namespace, @project, sample_commit.id) end step 'I click on another commit link' do - visit project_commit_path(@project, sample_commit.parent_id) + visit namespace_project_commit_path(@project.namespace, @project, sample_commit.parent_id) end step 'I have user with primary email' do diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb index 6b07b62f16f..6b85cf74f5f 100644 --- a/features/steps/project/create.rb +++ b/features/steps/project/create.rb @@ -9,7 +9,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps step 'I should see project page' do page.should have_content "Empty" - current_path.should == project_path(Project.last) + current_path.should == namespace_project_path(Project.last.namespace, Project.last) end step 'I should see empty project instuctions' do diff --git a/features/steps/project/deploy_keys.rb b/features/steps/project/deploy_keys.rb index 914da31322f..4bf5cb5fa40 100644 --- a/features/steps/project/deploy_keys.rb +++ b/features/steps/project/deploy_keys.rb @@ -24,7 +24,7 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps end step 'I should be on deploy keys page' do - current_path.should == project_deploy_keys_path(@project) + current_path.should == namespace_project_deploy_keys_path(@project.namespace, @project) end step 'I should see newly created deploy key' do diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index a5484ad3a00..63ad90e1241 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -23,7 +23,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps step 'I should see merge request "Merge Request On Forked Project"' do @project.merge_requests.size.should >= 1 @merge_request = @project.merge_requests.last - current_path.should == project_merge_request_path(@project, @merge_request) + current_path.should == namespace_project_merge_request_path(@project.namespace, @project, @merge_request) @merge_request.title.should == "Merge Request On Forked Project" @merge_request.source_project.should == @forked_project @merge_request.source_branch.should == "fix" @@ -64,7 +64,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I see prefilled new Merge Request page for the forked project' do - current_path.should == new_project_merge_request_path(@forked_project) + current_path.should == new_namespace_project_merge_request_path(@forked_project.namespace, @forked_project) find("#merge_request_source_project_id").value.should == @forked_project.id.to_s find("#merge_request_target_project_id").value.should == @project.id.to_s find("#merge_request_source_branch").value.should have_content "new_design" @@ -86,7 +86,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps page.should have_content "An Edited Forked Merge Request" @project.merge_requests.size.should >= 1 @merge_request = @project.merge_requests.last - current_path.should == project_merge_request_path(@project, @merge_request) + current_path.should == namespace_project_merge_request_path(@project.namespace, @project, @merge_request) @merge_request.source_project.should == @forked_project @merge_request.source_branch.should == "fix" @merge_request.target_branch.should == "master" @@ -106,7 +106,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I see the edit page prefilled for "Merge Request On Forked Project"' do - current_path.should == edit_project_merge_request_path(@project, @merge_request) + current_path.should == edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) page.should have_content "Edit merge request ##{@merge_request.id}" find("#merge_request_title").value.should == "Merge Request On Forked Project" end diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb index ba460ac8097..a2807c340f6 100644 --- a/features/steps/project/graph.rb +++ b/features/steps/project/graph.rb @@ -8,16 +8,16 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps When 'I visit project "Shop" graph page' do project = Project.find_by(name: "Shop") - visit project_graph_path(project, "master") + visit namespace_project_graph_path(project.namespace, project, "master") end step 'I visit project "Shop" commits graph page' do project = Project.find_by(name: "Shop") - visit commits_project_graph_path(project, "master") + visit commits_namespace_project_graph_path(project.namespace, project, "master") end step 'page should have commits graphs' do - page.should have_content "Commits statistic for master" + page.should have_content "Commit statistics for master" page.should have_content "Commits per day of month" end end diff --git a/features/steps/project/hooks.rb b/features/steps/project/hooks.rb index f4b8d372be8..4b135202593 100644 --- a/features/steps/project/hooks.rb +++ b/features/steps/project/hooks.rb @@ -29,7 +29,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps end step 'I should see newly created hook' do - current_path.should == project_hooks_path(current_project) + current_path.should == namespace_project_hooks_path(current_project.namespace, current_project) page.should have_content(@url) end @@ -44,7 +44,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps end step 'hook should be triggered' do - current_path.should == project_hooks_path(current_project) + current_path.should == namespace_project_hooks_path(current_project.namespace, current_project) page.should have_selector '.flash-notice', text: 'Hook successfully executed.' end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index c0ae5208541..6d72c93ad13 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -168,7 +168,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps When 'I visit empty project page' do project = Project.find_by(name: 'Empty Project') - visit project_path(project) + visit namespace_project_path(project.namespace, project) end step 'I see empty project details with ssh clone info' do @@ -180,7 +180,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps When "I visit empty project's issues page" do project = Project.find_by(name: 'Empty Project') - visit project_issues_path(project) + visit namespace_project_issues_path(project.namespace, project) end step 'I leave a comment with code block' do diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index 3e3e90824b4..6ce34c500c6 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -4,7 +4,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps include SharedPaths step 'I visit \'bug\' label edit page' do - visit edit_project_label_path(project, bug_label) + visit edit_namespace_project_label_path(project.namespace, project, bug_label) end step 'I remove label \'bug\'' do diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 6f421de1aba..b67b2e58caf 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -101,11 +101,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I switch to the diff tab' do - visit diffs_project_merge_request_path(project, merge_request) + visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request) end step 'I switch to the merge request\'s comments tab' do - visit project_merge_request_path(project, merge_request) + visit namespace_project_merge_request_path(project.namespace, project, merge_request) end step 'I click on the commit in the merge request' do @@ -213,7 +213,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should see a comment like "Line is wrong" in the second file' do - within '.files [id^=diff]:nth-child(2) .note-text' do + within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do page.should have_visible_content "Line is wrong" end end @@ -225,7 +225,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should see a comment like "Line is wrong here" in the second file' do - within '.files [id^=diff]:nth-child(2) .note-text' do + within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do page.should have_visible_content "Line is wrong here" end end @@ -238,7 +238,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps click_button "Add Comment" end - within ".files [id^=diff]:nth-child(1) .note-text" do + within ".files [id^=diff]:nth-child(1) .note-body > .note-text" do page.should have_content "Line is correct" end end @@ -253,7 +253,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should still see a comment like "Line is correct" in the first file' do - within '.files [id^=diff]:nth-child(1) .note-text' do + within '.files [id^=diff]:nth-child(1) .note-body > .note-text' do page.should have_visible_content "Line is correct" end end @@ -271,11 +271,15 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should see comments on the side-by-side diff page' do - within '.files [id^=diff]:nth-child(1) .parallel .note-text' do + within '.files [id^=diff]:nth-child(1) .parallel .note-body > .note-text' do page.should have_visible_content "Line is correct" end end + step 'I fill in merge request search with "Fe"' do + fill_in 'issue_search', with: "Fe" + end + def merge_request @merge_request ||= MergeRequest.find_by!(title: "Bug NS-05") end diff --git a/features/steps/project/network_graph.rb b/features/steps/project/network_graph.rb index 14fdc72b8b6..a15688ace6a 100644 --- a/features/steps/project/network_graph.rb +++ b/features/steps/project/network_graph.rb @@ -12,7 +12,7 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps Network::Graph.stub(max_count: 10) project = Project.find_by(name: "Shop") - visit project_network_path(project, "master") + visit namespace_project_network_path(project.namespace, project, "master") end step 'page should select "master" in select box' do diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index 033d45e0253..d39c8e7d2db 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -35,7 +35,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps end step 'I should see new project avatar' do - @project.avatar.should be_instance_of AttachmentUploader + @project.avatar.should be_instance_of AvatarUploader url = @project.avatar.url url.should == "/uploads/project/avatar/#{ @project.id }/gitlab_logo.png" end diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb index e54637120ce..57c6e39c801 100644 --- a/features/steps/project/redirects.rb +++ b/features/steps/project/redirects.rb @@ -13,11 +13,11 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps step 'I visit project "Community" page' do project = Project.find_by(name: 'Community') - visit project_path(project) + visit namespace_project_path(project.namespace, project) end step 'I should see project "Community" home page' do - Gitlab.config.gitlab.stub(:host).and_return("www.example.com") + Gitlab.config.gitlab.should_receive(:host).and_return("www.example.com") within '.navbar-gitlab .title' do page.should have_content 'Community' end @@ -25,12 +25,12 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps step 'I visit project "Enterprise" page' do project = Project.find_by(name: 'Enterprise') - visit project_path(project) + visit namespace_project_path(project.namespace, project) end step 'I visit project "CommunityDoesNotExist" page' do project = Project.find_by(name: 'Community') - visit project_path(project) + 'DoesNotExist' + visit namespace_project_path(project.namespace, project) + 'DoesNotExist' end step 'I click on "Sign In"' do diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 957a16d06a8..4b3d79324ab 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -4,7 +4,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps include SharedPaths step 'I visit project "Shop" services page' do - visit project_services_path(@project) + visit namespace_project_services_path(@project.namespace, @project) end step 'I should see list of available services' do @@ -17,6 +17,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps page.should have_content 'Atlassian Bamboo' page.should have_content 'JetBrains TeamCity' page.should have_content 'Asana' + page.should have_content 'Irker (IRC gateway)' end step 'I click gitlab-ci service link' do @@ -132,6 +133,22 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps find_field('Recipients').value.should == 'qa@company.name' end + step 'I click Irker service link' do + click_link 'Irker (IRC gateway)' + end + + step 'I fill Irker settings' do + check 'Active' + fill_in 'Recipients', with: 'irc://chat.freenode.net/#commits' + check 'Colorize messages' + click_button 'Save' + end + + step 'I should see Irker service settings saved' do + find_field('Recipients').value.should == 'irc://chat.freenode.net/#commits' + find_field('Colorize messages').value.should == '1' + end + step 'I click Slack service link' do click_link 'Slack' end diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb index 4a39bfdbb79..343aeb53b11 100644 --- a/features/steps/project/snippets.rb +++ b/features/steps/project/snippets.rb @@ -86,7 +86,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps end step 'I visit snippet page "Snippet one"' do - visit project_snippet_path(project, project_snippet) + visit namespace_project_snippet_path(project.namespace, project, project_snippet) end def project_snippet diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index 1fe01e55aa4..557555aee58 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -11,7 +11,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I should see files from repository for "6d39438"' do - current_path.should == project_tree_path(@project, "6d39438") + current_path.should == namespace_project_tree_path(@project.namespace, @project, "6d39438") page.should have_content ".gitignore" page.should have_content "LICENSE" end @@ -69,6 +69,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps fill_in :file_name, with: new_file_name end + step 'I fill the new branch name' do + fill_in :new_branch, with: 'new_branch_name' + end + step 'I fill the new file name with an illegal name' do fill_in :file_name, with: '.git' end @@ -141,21 +145,33 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I am redirected to the files URL' do - current_path.should == project_tree_path(@project, 'master') + current_path.should == namespace_project_tree_path(@project.namespace, @project, 'master') end step 'I am redirected to the ".gitignore"' do - expect(current_path).to eq(project_blob_path(@project, 'master/.gitignore')) + expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'master/.gitignore')) + end + + step 'I am redirected to the ".gitignore" on new branch' do + expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'new_branch_name/.gitignore')) end step 'I am redirected to the permalink URL' do - expect(current_path).to eq(project_blob_path( - @project, @project.repository.commit.sha + '/.gitignore')) + expect(current_path).to( + eq(namespace_project_blob_path(@project.namespace, @project, + @project.repository.commit.sha + + '/.gitignore')) + ) end step 'I am redirected to the new file' do - expect(current_path).to eq(project_blob_path( - @project, 'master/' + new_file_name)) + expect(current_path).to eq(namespace_project_blob_path( + @project.namespace, @project, 'master/' + new_file_name)) + end + + step 'I am redirected to the new file on new branch' do + expect(current_path).to eq(namespace_project_blob_path( + @project.namespace, @project, 'new_branch_name/' + new_file_name)) end step "I don't see the permalink link" do @@ -174,7 +190,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps click_link 'add a file' # Remove pre-receive hook so we can push without auth - FileUtils.rm(File.join(@project.repository.path, 'hooks', 'pre-receive')) + FileUtils.rm_f(File.join(@project.repository.path, 'hooks', 'pre-receive')) end private diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb index 53578ee5970..7961fdedad8 100644 --- a/features/steps/project/source/markdown_render.rb +++ b/features/steps/project/source/markdown_render.rb @@ -13,7 +13,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I should see files from repository in markdown' do - current_path.should == project_tree_path(@project, "markdown") + current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown") page.should have_content "README.md" page.should have_content "CHANGELOG" end @@ -33,7 +33,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I should see correct document rendered' do - current_path.should == project_blob_path(@project, "markdown/doc/api/README.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md") page.should have_content "All API requests require authentication" end @@ -42,7 +42,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I should see correct directory rendered' do - current_path.should == project_tree_path(@project, "markdown/doc/raketasks") + current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/raketasks") page.should have_content "backup_restore.md" page.should have_content "maintenance.md" end @@ -52,7 +52,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I should see correct doc/api directory rendered' do - current_path.should == project_tree_path(@project, "markdown/doc/api") + current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api") page.should have_content "README.md" page.should have_content "users.md" end @@ -62,7 +62,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I should see correct maintenance file rendered' do - current_path.should == project_blob_path(@project, "markdown/doc/raketasks/maintenance.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/raketasks/maintenance.md") page.should have_content "bundle exec rake gitlab:env:info RAILS_ENV=production" end @@ -93,7 +93,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I see correct file rendered' do - current_path.should == project_blob_path(@project, "markdown/doc/api/README.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md") page.should have_content "Contents" page.should have_link "Users" page.should have_link "Rake tasks" @@ -104,7 +104,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I should see the correct document file' do - current_path.should == project_blob_path(@project, "markdown/doc/api/users.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md") page.should have_content "Get a list of users." end @@ -115,100 +115,100 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps # Markdown branch When 'I visit markdown branch' do - visit project_tree_path(@project, "markdown") + visit namespace_project_tree_path(@project.namespace, @project, "markdown") end When 'I visit markdown branch "README.md" blob' do - visit project_blob_path(@project, "markdown/README.md") + visit namespace_project_blob_path(@project.namespace, @project, "markdown/README.md") end When 'I visit markdown branch "d" tree' do - visit project_tree_path(@project, "markdown/d") + visit namespace_project_tree_path(@project.namespace, @project, "markdown/d") end When 'I visit markdown branch "d/README.md" blob' do - visit project_blob_path(@project, "markdown/d/README.md") + visit namespace_project_blob_path(@project.namespace, @project, "markdown/d/README.md") end step 'I should see files from repository in markdown branch' do - current_path.should == project_tree_path(@project, "markdown") + current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown") page.should have_content "README.md" page.should have_content "CHANGELOG" end step 'I see correct file rendered in markdown branch' do - current_path.should == project_blob_path(@project, "markdown/doc/api/README.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md") page.should have_content "Contents" page.should have_link "Users" page.should have_link "Rake tasks" end step 'I should see correct document rendered for markdown branch' do - current_path.should == project_blob_path(@project, "markdown/doc/api/README.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md") page.should have_content "All API requests require authentication" end step 'I should see correct directory rendered for markdown branch' do - current_path.should == project_tree_path(@project, "markdown/doc/raketasks") + current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/raketasks") page.should have_content "backup_restore.md" page.should have_content "maintenance.md" end step 'I should see the users document file in markdown branch' do - current_path.should == project_blob_path(@project, "markdown/doc/api/users.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md") page.should have_content "Get a list of users." end # Expected link contents step 'The link with text "empty" should have url "tree/markdown"' do - find('a', text: /^empty$/)['href'] == current_host + project_tree_path(@project, "markdown") + find('a', text: /^empty$/)['href'] == current_host + namespace_project_tree_path(@project.namespace, @project, "markdown") end step 'The link with text "empty" should have url "blob/markdown/README.md"' do - find('a', text: /^empty$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + find('a', text: /^empty$/)['href'] == current_host + namespace_project_blob_path(@project.namespace, @project, "markdown/README.md") end step 'The link with text "empty" should have url "tree/markdown/d"' do - find('a', text: /^empty$/)['href'] == current_host + project_tree_path(@project, "markdown/d") + find('a', text: /^empty$/)['href'] == current_host + namespace_project_tree_path(@project.namespace, @project, "markdown/d") end step 'The link with text "empty" should have '\ 'url "blob/markdown/d/README.md"' do - find('a', text: /^empty$/)['href'] == current_host + project_blob_path(@project, "markdown/d/README.md") + find('a', text: /^empty$/)['href'] == current_host + namespace_project_blob_path(@project.namespace, @project, "markdown/d/README.md") end step 'The link with text "ID" should have url "tree/markdownID"' do - find('a', text: /^#id$/)['href'] == current_host + project_tree_path(@project, "markdown") + '#id' + find('a', text: /^#id$/)['href'] == current_host + namespace_project_tree_path(@project.namespace, @project, "markdown") + '#id' end step 'The link with text "/ID" should have url "tree/markdownID"' do - find('a', text: /^\/#id$/)['href'] == current_host + project_tree_path(@project, "markdown") + '#id' + find('a', text: /^\/#id$/)['href'] == current_host + namespace_project_tree_path(@project.namespace, @project, "markdown") + '#id' end step 'The link with text "README.mdID" '\ 'should have url "blob/markdown/README.mdID"' do - find('a', text: /^README.md#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id' + find('a', text: /^README.md#id$/)['href'] == current_host + namespace_project_blob_path(@project.namespace, @project, "markdown/README.md") + '#id' end step 'The link with text "d/README.mdID" should have '\ 'url "blob/markdown/d/README.mdID"' do - find('a', text: /^d\/README.md#id$/)['href'] == current_host + project_blob_path(@project, "d/markdown/README.md") + '#id' + find('a', text: /^d\/README.md#id$/)['href'] == current_host + namespace_project_blob_path(@project.namespace, @project, "d/markdown/README.md") + '#id' end step 'The link with text "ID" should have url "blob/markdown/README.mdID"' do - find('a', text: /^#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id' + find('a', text: /^#id$/)['href'] == current_host + namespace_project_blob_path(@project.namespace, @project, "markdown/README.md") + '#id' end step 'The link with text "/ID" should have url "blob/markdown/README.mdID"' do - find('a', text: /^\/#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id' + find('a', text: /^\/#id$/)['href'] == current_host + namespace_project_blob_path(@project.namespace, @project, "markdown/README.md") + '#id' end # Wiki step 'I go to wiki page' do click_link "Wiki" - current_path.should == project_wiki_path(@project, "home") + current_path.should == namespace_project_wiki_path(@project.namespace, @project, "home") end step 'I add various links to the wiki page' do @@ -218,7 +218,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'Wiki page should have added links' do - current_path.should == project_wiki_path(@project, "home") + current_path.should == namespace_project_wiki_path(@project.namespace, @project, "home") page.should have_content "test GitLab API doc Rake tasks" end @@ -237,13 +237,13 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I see new wiki page named test' do - current_path.should == project_wiki_path(@project, "test") + current_path.should == namespace_project_wiki_path(@project.namespace, @project, "test") page.should have_content "Editing" end When 'I go back to wiki page home' do - visit project_wiki_path(@project, "home") - current_path.should == project_wiki_path(@project, "home") + visit namespace_project_wiki_path(@project.namespace, @project, "home") + current_path.should == namespace_project_wiki_path(@project.namespace, @project, "home") end step 'I click on GitLab API doc link' do @@ -251,7 +251,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I see Gitlab API document' do - current_path.should == project_wiki_path(@project, "api") + current_path.should == namespace_project_wiki_path(@project.namespace, @project, "api") page.should have_content "Editing" end @@ -260,13 +260,13 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I see Rake tasks directory' do - current_path.should == project_wiki_path(@project, "raketasks") + current_path.should == namespace_project_wiki_path(@project.namespace, @project, "raketasks") page.should have_content "Editing" end step 'I go directory which contains README file' do - visit project_tree_path(@project, "markdown/doc/api") - current_path.should == project_tree_path(@project, "markdown/doc/api") + visit namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api") + current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api") end step 'I click on a relative link in README' do @@ -274,7 +274,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps end step 'I should see the correct markdown' do - current_path.should == project_blob_path(@project, "markdown/doc/api/users.md") + current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md") page.should have_content "List users" end diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index aa00818c602..cd7d5eac243 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -11,7 +11,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I should be redirected back to the Edit Home Wiki page' do - current_path.should == project_wiki_path(project, :home) + current_path.should == namespace_project_wiki_path(project.namespace, project, :home) end step 'I create the Wiki Home page' do @@ -33,7 +33,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I browse to that Wiki page' do - visit project_wiki_path(project, @page) + visit namespace_project_wiki_path(project.namespace, project, @page) end step 'I click on the Edit button' do @@ -50,7 +50,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I should be redirected back to that Wiki page' do - current_path.should == project_wiki_path(project, @page) + current_path.should == namespace_project_wiki_path(project.namespace, project, @page) end step 'That page has two revisions' do @@ -90,7 +90,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I browse to wiki page with images' do - visit project_wiki_path(project, @wiki_page) + visit namespace_project_wiki_path(project.namespace, project, @wiki_page) end step 'I click on existing image link' do diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb index c229864bc83..9beb688bd16 100644 --- a/features/steps/shared/active_tab.rb +++ b/features/steps/shared/active_tab.rb @@ -26,7 +26,7 @@ module SharedActiveTab end step 'the active main tab should be Home' do - ensure_active_main_tab('Activity') + ensure_active_main_tab('Your Projects') end step 'the active main tab should be Projects' do diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index 625bcc0b266..583746d4475 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -116,7 +116,7 @@ module SharedNote end step 'The comment with the header should not have an ID' do - within(".note-text") do + within(".note-body > .note-text") do page.should have_content("Comment with a header") page.should_not have_css("#comment-with-a-header") end @@ -135,4 +135,21 @@ module SharedNote 'li.note div.timeline-content input[type="checkbox"]' ) end + + step 'I edit the last comment with a +1' do + find(".note").hover + find('.js-note-edit').click + + within(".current-note-edit-form") do + fill_in 'note[note]', with: '+1 Awesome!' + click_button 'Save Comment' + sleep 0.05 + end + end + + step 'I should see +1 in the description' do + within(".note") do + page.should have_content("+1 Awesome!") + end + end end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index cef48c179b2..bb6c336d7cd 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -87,6 +87,18 @@ module SharedPaths visit help_path end + step 'I visit dashboard groups page' do + visit dashboard_groups_path + end + + step 'I should be redirected to the dashboard groups page' do + current_path.should == dashboard_groups_path + end + + step 'I visit dashboard starred projects page' do + visit starred_dashboard_projects_path + end + # ---------------------------------------- # Profile # ---------------------------------------- @@ -119,14 +131,6 @@ module SharedPaths visit history_profile_path end - step 'I visit profile groups page' do - visit profile_groups_path - end - - step 'I should be redirected to the profile groups page' do - current_path.should == profile_groups_path - end - # ---------------------------------------- # Admin # ---------------------------------------- @@ -136,7 +140,7 @@ module SharedPaths end step 'I visit admin projects page' do - visit admin_projects_path + visit admin_namespaces_projects_path end step 'I visit admin users page' do @@ -180,59 +184,59 @@ module SharedPaths # ---------------------------------------- step "I visit my project's home page" do - visit project_path(@project) + visit namespace_project_path(@project.namespace, @project) end step "I visit my project's settings page" do - visit edit_project_path(@project) + visit edit_namespace_project_path(@project.namespace, @project) end step "I visit my project's files page" do - visit project_tree_path(@project, root_ref) + visit namespace_project_tree_path(@project.namespace, @project, root_ref) end step 'I visit a binary file in the repo' do - visit project_blob_path(@project, File.join( + visit namespace_project_blob_path(@project.namespace, @project, File.join( root_ref, 'files/images/logo-black.png')) end step "I visit my project's commits page" do - visit project_commits_path(@project, root_ref, {limit: 5}) + visit namespace_project_commits_path(@project.namespace, @project, root_ref, {limit: 5}) end step "I visit my project's commits page for a specific path" do - visit project_commits_path(@project, root_ref + "/app/models/project.rb", {limit: 5}) + visit namespace_project_commits_path(@project.namespace, @project, root_ref + "/app/models/project.rb", {limit: 5}) end step 'I visit my project\'s commits stats page' do - visit stats_project_repository_path(@project) + visit stats_namespace_project_repository_path(@project.namespace, @project) end step "I visit my project's network page" do # Stub Graph max_size to speed up test (10 commits vs. 650) Network::Graph.stub(max_count: 10) - visit project_network_path(@project, root_ref) + visit namespace_project_network_path(@project.namespace, @project, root_ref) end step "I visit my project's issues page" do - visit project_issues_path(@project) + visit namespace_project_issues_path(@project.namespace, @project) end step "I visit my project's merge requests page" do - visit project_merge_requests_path(@project) + visit namespace_project_merge_requests_path(@project.namespace, @project) end step "I visit my project's wiki page" do - visit project_wiki_path(@project, :home) + visit namespace_project_wiki_path(@project.namespace, @project, :home) end step 'I visit project hooks page' do - visit project_hooks_path(@project) + visit namespace_project_hooks_path(@project.namespace, @project) end step 'I visit project deploy keys page' do - visit project_deploy_keys_path(@project) + visit namespace_project_deploy_keys_path(@project.namespace, @project) end # ---------------------------------------- @@ -240,153 +244,153 @@ module SharedPaths # ---------------------------------------- step 'I visit project "Shop" page' do - visit project_path(project) + visit namespace_project_path(project.namespace, project) end step 'I visit project "Forked Shop" merge requests page' do - visit project_merge_requests_path(@forked_project) + visit namespace_project_merge_requests_path(@forked_project.namespace, @forked_project) end step 'I visit edit project "Shop" page' do - visit edit_project_path(project) + visit edit_namespace_project_path(project.namespace, project) end step 'I visit project branches page' do - visit project_branches_path(@project) + visit namespace_project_branches_path(@project.namespace, @project) end step 'I visit project protected branches page' do - visit project_protected_branches_path(@project) + visit namespace_project_protected_branches_path(@project.namespace, @project) end step 'I visit compare refs page' do - visit project_compare_index_path(@project) + visit namespace_project_compare_index_path(@project.namespace, @project) end step 'I visit project commits page' do - visit project_commits_path(@project, root_ref, {limit: 5}) + visit namespace_project_commits_path(@project.namespace, @project, root_ref, {limit: 5}) end step 'I visit project commits page for stable branch' do - visit project_commits_path(@project, 'stable', {limit: 5}) + visit namespace_project_commits_path(@project.namespace, @project, 'stable', {limit: 5}) end step 'I visit project source page' do - visit project_tree_path(@project, root_ref) + visit namespace_project_tree_path(@project.namespace, @project, root_ref) end step 'I visit blob file from repo' do - visit project_blob_path(@project, File.join(sample_commit.id, sample_blob.path)) + visit namespace_project_blob_path(@project.namespace, @project, File.join(sample_commit.id, sample_blob.path)) end step 'I visit ".gitignore" file in repo' do - visit project_blob_path(@project, File.join(root_ref, '.gitignore')) + visit namespace_project_blob_path(@project.namespace, @project, File.join(root_ref, '.gitignore')) end step 'I am on the new file page' do - current_path.should eq(project_create_blob_path(@project, root_ref)) + current_path.should eq(namespace_project_create_blob_path(@project.namespace, @project, root_ref)) end step 'I am on the ".gitignore" edit file page' do - current_path.should eq(project_edit_blob_path( - @project, File.join(root_ref, '.gitignore'))) + current_path.should eq(namespace_project_edit_blob_path( + @project.namespace, @project, File.join(root_ref, '.gitignore'))) end step 'I visit project source page for "6d39438"' do - visit project_tree_path(@project, "6d39438") + visit namespace_project_tree_path(@project.namespace, @project, "6d39438") end step 'I visit project source page for' \ ' "6d394385cf567f80a8fd85055db1ab4c5295806f"' do - visit project_tree_path(@project, + visit namespace_project_tree_path(@project.namespace, @project, '6d394385cf567f80a8fd85055db1ab4c5295806f') end step 'I visit project tags page' do - visit project_tags_path(@project) + visit namespace_project_tags_path(@project.namespace, @project) end step 'I visit project commit page' do - visit project_commit_path(@project, sample_commit.id) + visit namespace_project_commit_path(@project.namespace, @project, sample_commit.id) end step 'I visit project "Shop" issues page' do - visit project_issues_path(project) + visit namespace_project_issues_path(project.namespace, project) end step 'I visit issue page "Release 0.4"' do issue = Issue.find_by(title: "Release 0.4") - visit project_issue_path(issue.project, issue) + visit namespace_project_issue_path(issue.project.namespace, issue.project, issue) end step 'I visit issue page "Tasks-open"' do issue = Issue.find_by(title: 'Tasks-open') - visit project_issue_path(issue.project, issue) + visit namespace_project_issue_path(issue.project.namespace, issue.project, issue) end step 'I visit issue page "Tasks-closed"' do issue = Issue.find_by(title: 'Tasks-closed') - visit project_issue_path(issue.project, issue) + visit namespace_project_issue_path(issue.project.namespace, issue.project, issue) end step 'I visit project "Shop" labels page' do project = Project.find_by(name: 'Shop') - visit project_labels_path(project) + visit namespace_project_labels_path(project.namespace, project) end step 'I visit project "Forum" labels page' do project = Project.find_by(name: 'Forum') - visit project_labels_path(project) + visit namespace_project_labels_path(project.namespace, project) end step 'I visit project "Shop" new label page' do project = Project.find_by(name: 'Shop') - visit new_project_label_path(project) + visit new_namespace_project_label_path(project.namespace, project) end step 'I visit project "Forum" new label page' do project = Project.find_by(name: 'Forum') - visit new_project_label_path(project) + visit new_namespace_project_label_path(project.namespace, project) end step 'I visit merge request page "Bug NS-04"' do mr = MergeRequest.find_by(title: "Bug NS-04") - visit project_merge_request_path(mr.target_project, mr) + visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) end step 'I visit merge request page "Bug NS-05"' do mr = MergeRequest.find_by(title: "Bug NS-05") - visit project_merge_request_path(mr.target_project, mr) + visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) end step 'I visit merge request page "MR-task-open"' do mr = MergeRequest.find_by(title: 'MR-task-open') - visit project_merge_request_path(mr.target_project, mr) + visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) end step 'I visit merge request page "MR-task-closed"' do mr = MergeRequest.find_by(title: 'MR-task-closed') - visit project_merge_request_path(mr.target_project, mr) + visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) end step 'I visit project "Shop" merge requests page' do - visit project_merge_requests_path(project) + visit namespace_project_merge_requests_path(project.namespace, project) end step 'I visit forked project "Shop" merge requests page' do - visit project_merge_requests_path(project) + visit namespace_project_merge_requests_path(project.namespace, project) end step 'I visit project "Shop" milestones page' do - visit project_milestones_path(project) + visit namespace_project_milestones_path(project.namespace, project) end step 'I visit project "Shop" team page' do - visit project_team_index_path(project) + visit namespace_project_team_index_path(project.namespace, project) end step 'I visit project wiki page' do - visit project_wiki_path(@project, :home) + visit namespace_project_wiki_path(@project.namespace, @project, :home) end # ---------------------------------------- @@ -395,22 +399,22 @@ module SharedPaths step 'I visit project "Community" page' do project = Project.find_by(name: "Community") - visit project_path(project) + visit namespace_project_path(project.namespace, project) end step 'I visit project "Community" source page' do project = Project.find_by(name: 'Community') - visit project_tree_path(project, root_ref) + visit namespace_project_tree_path(project.namespace, project, root_ref) end step 'I visit project "Internal" page' do project = Project.find_by(name: "Internal") - visit project_path(project) + visit namespace_project_path(project.namespace, project) end step 'I visit project "Enterprise" page' do project = Project.find_by(name: "Enterprise") - visit project_path(project) + visit namespace_project_path(project.namespace, project) end # ---------------------------------------- @@ -419,7 +423,7 @@ module SharedPaths step "I visit empty project page" do project = Project.find_by(name: "Empty Public Project") - visit project_path(project) + visit namespace_project_path(project.namespace, project) end # ---------------------------------------- @@ -447,7 +451,7 @@ module SharedPaths # ---------------------------------------- step 'I visit project "Shop" snippets page' do - visit project_snippets_path(project) + visit namespace_project_snippets_path(project.namespace, project) end step 'I visit snippets page' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index cf0be256231..41f71ae29cb 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -29,7 +29,8 @@ module SharedProject end step 'I visit my empty project page' do - visit project_path(Project.find_by(name: 'Empty Project')) + project = Project.find_by(name: 'Empty Project') + visit namespace_project_path(project.namespace, project) end step 'project "Shop" has push event' do @@ -64,7 +65,7 @@ module SharedProject end step 'I should see project settings' do - current_path.should == edit_project_path(@project) + current_path.should == edit_namespace_project_path(@project.namespace, @project) page.should have_content("Project name") page.should have_content("Features:") end diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb index 6aa4f1b20df..c5aed19331c 100644 --- a/features/steps/shared/project_tab.rb +++ b/features/steps/shared/project_tab.rb @@ -41,6 +41,8 @@ module SharedProjectTab end step 'the active main tab should be Settings' do - ensure_active_main_tab('Settings') + within '.nav-sidebar' do + page.should have_content('Back to project') + end end end diff --git a/features/support/env.rb b/features/support/env.rb index 67660777842..be17065ccfd 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -47,8 +47,8 @@ Spinach.hooks.after_scenario do end Spinach.hooks.before_run do + include RSpec::Mocks::ExampleMethods TestEnv.init(mailer: false) - RSpec::Mocks::setup self include FactoryGirl::Syntax::Methods end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8d0664386b4..489be210784 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -56,6 +56,7 @@ module API expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at expose :namespace expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? } + expose :avatar_url end class ProjectMember < UserBasic @@ -65,7 +66,7 @@ module API end class Group < Grape::Entity - expose :id, :name, :path, :owner_id, :description + expose :id, :name, :path, :description end class GroupDetail < Group @@ -142,7 +143,7 @@ module API class ProjectEntity < Grape::Entity expose :id, :iid - expose (:project_id) { |entity| entity.project.id } + expose(:project_id) { |entity| entity.project.id } expose :title, :description expose :state, :created_at, :updated_at end diff --git a/lib/api/files.rb b/lib/api/files.rb index e6e71bac367..3176ef0e256 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -117,7 +117,8 @@ module API branch_name: branch_name } else - render_api_error!(result[:message], 400) + http_status = result[:http_status] || 400 + render_api_error!(result[:message], http_status) end end diff --git a/lib/api/group_members.rb b/lib/api/group_members.rb index 4373070083a..c9c9ccbcb2e 100644 --- a/lib/api/group_members.rb +++ b/lib/api/group_members.rb @@ -40,6 +40,30 @@ module API present member.user, with: Entities::GroupMember, group: group end + # Update group member + # + # Parameters: + # id (required) - The ID of a group + # user_id (required) - The ID of a group member + # access_level (required) - Project access level + # Example Request: + # PUT /groups/:id/members/:user_id + put ':id/members/:user_id' do + group = find_group(params[:id]) + authorize! :manage_group, group + required_attributes! [:access_level] + + team_member = group.group_members.find_by(user_id: params[:user_id]) + not_found!('User can not be found') if team_member.nil? + + if team_member.update_attributes(access_level: params[:access_level]) + @member = team_member.user + present @member, with: Entities::GroupMember, group: group + else + handle_member_errors team_member.errors + end + end + # Remove member. # # Parameters: diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 384a28e41f5..a92abd4b690 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -33,9 +33,9 @@ module API attrs = attributes_for_keys [:name, :path, :description] @group = Group.new(attrs) - @group.owner = current_user if @group.save + @group.add_owner(current_user) present @group, with: Entities::Group else render_api_error!("Failed to save group #{@group.errors.messages}", 400) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 8fa30460ba6..ee678d84c84 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -83,7 +83,10 @@ module API end def authenticate_by_gitlab_shell_token! - unauthorized! unless secret_token == params['secret_token'] + input = params['secret_token'].try(:chomp) + unless Devise.secure_compare(secret_token, input) + unauthorized! + end end def authenticated_as_admin! @@ -236,7 +239,12 @@ module API end def secret_token - File.read(Rails.root.join('.gitlab_shell_secret')) + File.read(Rails.root.join('.gitlab_shell_secret')).chomp + end + + def handle_member_errors(errors) + error!(errors[:access_level], 422) if errors[:access_level].any? + not_found!(errors) end end end diff --git a/lib/api/internal.rb b/lib/api/internal.rb index b5542c1874b..753d0fcbd98 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -16,6 +16,17 @@ module API # post "/allowed" do status 200 + + actor = if params[:key_id] + Key.find_by(id: params[:key_id]) + elsif params[:user_id] + User.find_by(id: params[:user_id]) + end + + unless actor + return Gitlab::GitAccessStatus.new(false, 'No such user or key') + end + project_path = params[:project] # Check for *.wiki repositories. @@ -32,26 +43,20 @@ module API project = Project.find_with_namespace(project_path) - unless project - return Gitlab::GitAccessStatus.new(false, 'No such project') + if project + status = access.check( + actor, + params[:action], + project, + params[:changes] + ) end - actor = if params[:key_id] - Key.find_by(id: params[:key_id]) - elsif params[:user_id] - User.find_by(id: params[:user_id]) - end - - unless actor - return Gitlab::GitAccessStatus.new(false, 'No such user or key') + if project && status && status.allowed? + status + else + Gitlab::GitAccessStatus.new(false, 'No such project') end - - access.check( - actor, - params[:action], - project, - params[:changes] - ) end # @@ -74,7 +79,7 @@ module API if message = BroadcastMessage.current present message, with: Entities::BroadcastMessage else - not_found! + {} end end end diff --git a/lib/api/project_members.rb b/lib/api/project_members.rb index 1e890f9e199..73cf062155b 100644 --- a/lib/api/project_members.rb +++ b/lib/api/project_members.rb @@ -4,14 +4,6 @@ module API before { authenticate! } resource :projects do - helpers do - def handle_project_member_errors(errors) - if errors[:access_level].any? - error!(errors[:access_level], 422) - end - not_found!(errors) - end - end # Get a project team members # @@ -66,7 +58,7 @@ module API @member = team_member.user present @member, with: Entities::ProjectMember, project: user_project else - handle_project_member_errors team_member.errors + handle_member_errors team_member.errors end end @@ -89,7 +81,7 @@ module API @member = team_member.user present @member, with: Entities::ProjectMember, project: user_project else - handle_project_member_errors team_member.errors + handle_member_errors team_member.errors end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 37b36ddcf94..7c8b3250cd0 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -54,15 +54,18 @@ module API # bio - Bio # admin - User is admin - true or false (default) # can_create_group - User can create groups - true or false + # confirm - Require user confirmation - true (default) or false # Example Request: # POST /users post do authenticated_as_admin! required_attributes! [:email, :password, :name, :username] - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :can_create_group, :admin] + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :can_create_group, :admin, :confirm] user = User.build_user(attrs) admin = attrs.delete(:admin) user.admin = admin unless admin.nil? + confirm = !(attrs.delete(:confirm) =~ (/(false|f|no|0)$/i)) + user.skip_confirmation! unless confirm identity_attrs = attributes_for_keys [:provider, :extern_uid] if identity_attrs.any? diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index 19215cfb7e6..6e4ed01e079 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -102,7 +102,8 @@ module ExtractsPath raise InvalidPathError unless @commit @hex_path = Digest::SHA1.hexdigest(@path) - @logs_path = logs_file_project_ref_path(@project, @ref, @path) + @logs_path = logs_file_namespace_project_ref_path(@project.namespace, + @project, @ref, @path) rescue RuntimeError, NoMethodError, InvalidPathError not_found! diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 3f207c56631..ee877e099b1 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -10,8 +10,9 @@ module Grack @request = Rack::Request.new(env) @auth = Request.new(env) - # Need this patch due to the rails mount + @gitlab_ci = false + # Need this patch due to the rails mount # Need this if under RELATIVE_URL_ROOT unless Gitlab.config.gitlab.relative_url_root.empty? # If website is mounted using relative_url_root need to remove it first @@ -22,8 +23,12 @@ module Grack @env['SCRIPT_NAME'] = "" - if project - auth! + auth! + + if project && authorized_request? + @app.call(env) + elsif @user.nil? && !@gitlab_ci + unauthorized else render_not_found end @@ -32,35 +37,30 @@ module Grack private def auth! - if @auth.provided? - return bad_request unless @auth.basic? - - # Authentication with username and password - login, password = @auth.credentials + return unless @auth.provided? - # Allow authentication for GitLab CI service - # if valid token passed - if gitlab_ci_request?(login, password) - return @app.call(env) - end + return bad_request unless @auth.basic? - @user = authenticate_user(login, password) + # Authentication with username and password + login, password = @auth.credentials - if @user - Gitlab::ShellEnv.set_env(@user) - @env['REMOTE_USER'] = @auth.username - end + # Allow authentication for GitLab CI service + # if valid token passed + if gitlab_ci_request?(login, password) + @gitlab_ci = true + return end - if authorized_request? - @app.call(env) - else - unauthorized + @user = authenticate_user(login, password) + + if @user + Gitlab::ShellEnv.set_env(@user) + @env['REMOTE_USER'] = @auth.username end end def gitlab_ci_request?(login, password) - if login == "gitlab-ci-token" && project.gitlab_ci? + if login == "gitlab-ci-token" && project && project.gitlab_ci? token = project.gitlab_ci_service.token if token.present? && token == password && git_cmd == 'git-upload-pack' @@ -107,6 +107,8 @@ module Grack end def authorized_request? + return true if @gitlab_ci + case git_cmd when *Gitlab::GitAccess::DOWNLOAD_COMMANDS if user @@ -141,7 +143,9 @@ module Grack end def project - @project ||= project_by_path(@request.path_info) + return @project if defined?(@project) + + @project = project_by_path(@request.path_info) end def project_by_path(path) @@ -149,6 +153,7 @@ module Grack path_with_namespace = m.last path_with_namespace.gsub!(/\.wiki$/, '') + path_with_namespace[0] = '' if path_with_namespace.start_with?('/') Project.find_with_namespace(path_with_namespace) end end diff --git a/lib/gitlab/bitbucket_import.rb b/lib/gitlab/bitbucket_import.rb new file mode 100644 index 00000000000..7298152e7e9 --- /dev/null +++ b/lib/gitlab/bitbucket_import.rb @@ -0,0 +1,6 @@ +module Gitlab + module BitbucketImport + mattr_accessor :public_key + @public_key = nil + end +end diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb new file mode 100644 index 00000000000..c907bebaef6 --- /dev/null +++ b/lib/gitlab/bitbucket_import/client.rb @@ -0,0 +1,99 @@ +module Gitlab + module BitbucketImport + class Client + attr_reader :consumer, :api + + def initialize(access_token = nil, access_token_secret = nil) + @consumer = ::OAuth::Consumer.new( + config.app_id, + config.app_secret, + bitbucket_options + ) + + if access_token && access_token_secret + @api = ::OAuth::AccessToken.new(@consumer, access_token, access_token_secret) + end + end + + def request_token(redirect_uri) + request_token = consumer.get_request_token(oauth_callback: redirect_uri) + + { + oauth_token: request_token.token, + oauth_token_secret: request_token.secret, + oauth_callback_confirmed: request_token.callback_confirmed?.to_s + } + end + + def authorize_url(request_token, redirect_uri) + request_token = ::OAuth::RequestToken.from_hash(consumer, request_token) if request_token.is_a?(Hash) + + if request_token.callback_confirmed? + request_token.authorize_url + else + request_token.authorize_url(oauth_callback: redirect_uri) + end + end + + def get_token(request_token, oauth_verifier, redirect_uri) + request_token = ::OAuth::RequestToken.from_hash(consumer, request_token) if request_token.is_a?(Hash) + + if request_token.callback_confirmed? + request_token.get_access_token(oauth_verifier: oauth_verifier) + else + request_token.get_access_token(oauth_callback: redirect_uri) + end + end + + def user + JSON.parse(api.get("/api/1.0/user").body) + end + + def issues(project_identifier) + JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/issues").body) + end + + def issue_comments(project_identifier, issue_id) + JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body) + end + + def project(project_identifier) + JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}").body) + end + + def find_deploy_key(project_identifier, key) + JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/deploy-keys").body).find do |deploy_key| + deploy_key["key"].chomp == key.chomp + end + end + + def add_deploy_key(project_identifier, key) + deploy_key = find_deploy_key(project_identifier, key) + return if deploy_key + + JSON.parse(api.post("/api/1.0/repositories/#{project_identifier}/deploy-keys", key: key, label: "GitLab import key").body) + end + + def delete_deploy_key(project_identifier, key) + deploy_key = find_deploy_key(project_identifier, key) + return unless deploy_key + + api.delete("/api/1.0/repositories/#{project_identifier}/deploy-keys/#{deploy_key["pk"]}").code == "204" + end + + def projects + JSON.parse(api.get("/api/1.0/user/repositories").body).select { |repo| repo["scm"] == "git" } + end + + private + + def config + Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket"} + end + + def bitbucket_options + OmniAuth::Strategies::Bitbucket.default_options[:client_options] + end + end + end +end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb new file mode 100644 index 00000000000..42c93707caa --- /dev/null +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -0,0 +1,52 @@ +module Gitlab + module BitbucketImport + class Importer + attr_reader :project, :client + + def initialize(project) + @project = project + @client = Client.new(project.creator.bitbucket_access_token, project.creator.bitbucket_access_token_secret) + @formatter = Gitlab::ImportFormatter.new + end + + def execute + project_identifier = project.import_source + + return true unless client.project(project_identifier)["has_issues"] + + #Issues && Comments + issues = client.issues(project_identifier) + + issues["issues"].each do |issue| + body = @formatter.author_line(issue["reported_by"]["username"], issue["content"]) + + comments = client.issue_comments(project_identifier, issue["local_id"]) + + if comments.any? + body += @formatter.comments_header + end + + comments.each do |comment| + body += @formatter.comment(comment["author_info"]["username"], comment["utc_created_on"], comment["content"]) + end + + project.issues.create!( + description: body, + title: issue["title"], + state: %w(resolved invalid duplicate wontfix).include?(issue["status"]) ? 'closed' : 'opened', + author_id: gl_user_id(project, issue["reported_by"]["username"]) + ) + end + + true + end + + private + + def gl_user_id(project, bitbucket_id) + user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s) + (user && user.id) || project.creator_id + end + end + end +end diff --git a/lib/gitlab/bitbucket_import/key_adder.rb b/lib/gitlab/bitbucket_import/key_adder.rb new file mode 100644 index 00000000000..9931aa7e029 --- /dev/null +++ b/lib/gitlab/bitbucket_import/key_adder.rb @@ -0,0 +1,23 @@ +module Gitlab + module BitbucketImport + class KeyAdder + attr_reader :repo, :current_user, :client + + def initialize(repo, current_user) + @repo, @current_user = repo, current_user + @client = Client.new(current_user.bitbucket_access_token, current_user.bitbucket_access_token_secret) + end + + def execute + return false unless BitbucketImport.public_key.present? + + project_identifier = "#{repo["owner"]}/#{repo["slug"]}" + client.add_deploy_key(project_identifier, BitbucketImport.public_key) + + true + rescue + false + end + end + end +end diff --git a/lib/gitlab/bitbucket_import/key_deleter.rb b/lib/gitlab/bitbucket_import/key_deleter.rb new file mode 100644 index 00000000000..1a24a86fc37 --- /dev/null +++ b/lib/gitlab/bitbucket_import/key_deleter.rb @@ -0,0 +1,23 @@ +module Gitlab + module BitbucketImport + class KeyDeleter + attr_reader :project, :current_user, :client + + def initialize(project) + @project = project + @current_user = project.creator + @client = Client.new(current_user.bitbucket_access_token, current_user.bitbucket_access_token_secret) + end + + def execute + return false unless BitbucketImport.public_key.present? + + client.delete_deploy_key(project.import_source, BitbucketImport.public_key) + + true + rescue + false + end + end + end +end diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb new file mode 100644 index 00000000000..db33af2c2da --- /dev/null +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -0,0 +1,39 @@ +module Gitlab + module BitbucketImport + class ProjectCreator + attr_reader :repo, :namespace, :current_user + + def initialize(repo, namespace, current_user) + @repo = repo + @namespace = namespace + @current_user = current_user + end + + def execute + @project = Project.new( + name: repo["name"], + path: repo["slug"], + description: repo["description"], + namespace: namespace, + creator: current_user, + visibility_level: repo["is_private"] ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::VisibilityLevel::PUBLIC, + import_type: "bitbucket", + import_source: "#{repo["owner"]}/#{repo["slug"]}", + import_url: "ssh://git@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}.git" + ) + + if @project.save! + @project.reload + + if @project.import_failed? + @project.import_retry + else + @project.import_start + end + end + + @project + end + end + end +end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 93e7edf508c..1a25eebe7d1 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -1,11 +1,15 @@ module Gitlab module CurrentSettings def current_application_settings - if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('application_settings') - ApplicationSetting.current || - ApplicationSetting.create_from_defaults - else - fake_application_settings + key = :current_application_settings + + RequestStore.store[key] ||= begin + if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('application_settings') + RequestStore.store[:current_application_settings] = + (ApplicationSetting.current || ApplicationSetting.create_from_defaults) + else + fake_application_settings + end end end diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb index 887ed76b36c..c1d9520ddf1 100644 --- a/lib/gitlab/diff/parser.rb +++ b/lib/gitlab/diff/parser.rb @@ -27,7 +27,7 @@ module Gitlab line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0 - next if line_old == 1 && line_new == 1 #top of file + next if line_old <= 1 && line_new <= 1 #top of file lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new) line_obj_index += 1 next diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb index 4a712c6345f..0c350d7c675 100644 --- a/lib/gitlab/git.rb +++ b/lib/gitlab/git.rb @@ -1,9 +1,25 @@ module Gitlab module Git BLANK_SHA = '0' * 40 + TAG_REF_PREFIX = "refs/tags/" + BRANCH_REF_PREFIX = "refs/heads/" - def self.extract_ref_name(ref) - ref.gsub(/\Arefs\/(tags|heads)\//, '') + class << self + def ref_name(ref) + ref.gsub(/\Arefs\/(tags|heads)\//, '') + end + + def tag_ref?(ref) + ref.start_with?(TAG_REF_PREFIX) + end + + def branch_ref?(ref) + ref.start_with?(BRANCH_REF_PREFIX) + end + + def blank_ref?(ref) + ref == BLANK_SHA + end end end end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 6444cec7eb5..cb69e4b13d3 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -6,6 +6,8 @@ module Gitlab attr_reader :params, :project, :git_cmd, :user def self.can_push_to_branch?(user, project, ref) + return false unless user + if project.protected_branch?(ref) && !(project.developers_can_push_to_protected_branch?(ref) && project.team.developer?(user)) user.can?(:push_code_to_protected_branches, project) @@ -113,7 +115,7 @@ module Gitlab # we dont allow force push to protected branch if forced_push?(project, oldrev, newrev) :force_push_code_to_protected_branches - elsif newrev == Gitlab::Git::BLANK_SHA + elsif Gitlab::Git.blank_ref?(newrev) # and we dont allow remove of protected branch :remove_protected_branches elsif project.developers_can_push_to_protected_branch?(branch_name) @@ -133,8 +135,8 @@ module Gitlab def branch_name(ref) ref = ref.to_s - if ref.start_with?('refs/heads') - ref.sub(%r{\Arefs/heads/}, '') + if Gitlab::Git.branch_ref?(ref) + Gitlab::Git.ref_name(ref) else nil end @@ -142,8 +144,8 @@ module Gitlab def tag_name(ref) ref = ref.to_s - if ref.start_with?('refs/tags') - ref.sub(%r{\Arefs/tags/}, '') + if Gitlab::Git.tag_ref?(ref) + Gitlab::Git.ref_name(ref) else nil end diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index c9904fe8779..676d226bddd 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -46,11 +46,7 @@ module Gitlab end def github_options - { - site: 'https://api.github.com', - authorize_url: 'https://github.com/login/oauth/authorize', - token_url: 'https://github.com/login/oauth/access_token' - } + OmniAuth::Strategies::GitHub.default_options[:client_options] end end end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index bc2b645b2d9..23832b3233c 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -20,7 +20,7 @@ module Gitlab body += @formatter.comments_header client.issue_comments(project.import_source, issue.number).each do |c| - body += @formatter.comment_to_md(c.user.login, c.created_at, c.body) + body += @formatter.comment(c.user.login, c.created_at, c.body) end end diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb index 2206b68da99..ecf4ff94e39 100644 --- a/lib/gitlab/gitlab_import/client.rb +++ b/lib/gitlab/gitlab_import/client.rb @@ -9,7 +9,7 @@ module Gitlab @client = ::OAuth2::Client.new( config.app_id, config.app_secret, - github_options + gitlab_options ) if access_token @@ -70,12 +70,8 @@ module Gitlab Gitlab.config.omniauth.providers.find{|provider| provider.name == "gitlab"} end - def github_options - { - site: 'https://gitlab.com/', - authorize_url: 'oauth/authorize', - token_url: 'oauth/token' - } + def gitlab_options + OmniAuth::Strategies::GitLab.default_options[:client_options] end end end diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb index 5f9b14399a4..c5304a0699b 100644 --- a/lib/gitlab/gitlab_import/importer.rb +++ b/lib/gitlab/gitlab_import/importer.rb @@ -25,7 +25,7 @@ module Gitlab end comments.each do |comment| - body += @formatter.comment_to_md(comment["author"]["name"], comment["created_at"], comment["body"]) + body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"]) end project.issues.create!( diff --git a/lib/gitlab/gitorious_import/client.rb b/lib/gitlab/gitorious_import/client.rb new file mode 100644 index 00000000000..5043f6a2ebd --- /dev/null +++ b/lib/gitlab/gitorious_import/client.rb @@ -0,0 +1,63 @@ +module Gitlab + module GitoriousImport + GITORIOUS_HOST = "https://gitorious.org" + + class Client + attr_reader :repo_list + + def initialize(repo_list) + @repo_list = repo_list + end + + def authorize_url(redirect_uri) + "#{GITORIOUS_HOST}/gitlab-import?callback_url=#{redirect_uri}" + end + + def repos + @repos ||= repo_names.map { |full_name| Repository.new(full_name) } + end + + def repo(id) + repos.find { |repo| repo.id == id } + end + + private + + def repo_names + repo_list.to_s.split(',').map(&:strip).reject(&:blank?) + end + end + + Repository = Struct.new(:full_name) do + def id + Digest::SHA1.hexdigest(full_name) + end + + def namespace + segments.first + end + + def path + segments.last + end + + def name + path.titleize + end + + def description + "" + end + + def import_url + "#{GITORIOUS_HOST}/#{full_name}.git" + end + + private + + def segments + full_name.split('/') + end + end + end +end diff --git a/lib/gitlab/gitorious_import/project_creator.rb b/lib/gitlab/gitorious_import/project_creator.rb new file mode 100644 index 00000000000..3cbebe53997 --- /dev/null +++ b/lib/gitlab/gitorious_import/project_creator.rb @@ -0,0 +1,39 @@ +module Gitlab + module GitoriousImport + class ProjectCreator + attr_reader :repo, :namespace, :current_user + + def initialize(repo, namespace, current_user) + @repo = repo + @namespace = namespace + @current_user = current_user + end + + def execute + @project = Project.new( + name: repo.name, + path: repo.path, + description: repo.description, + namespace: namespace, + creator: current_user, + visibility_level: Gitlab::VisibilityLevel::PUBLIC, + import_type: "gitorious", + import_source: repo.full_name, + import_url: repo.import_url + ) + + if @project.save! + @project.reload + + if @project.import_failed? + @project.import_retry + else + @project.import_start + end + end + + @project + end + end + end +end diff --git a/lib/gitlab/import_formatter.rb b/lib/gitlab/import_formatter.rb index ebb4b87f7e3..72e041a90b1 100644 --- a/lib/gitlab/import_formatter.rb +++ b/lib/gitlab/import_formatter.rb @@ -1,6 +1,6 @@ module Gitlab class ImportFormatter - def comment_to_md(author, date, body) + def comment(author, date, body) "\n\n*By #{author} on #{date}*\n\n#{body}" end diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb index 0c85acf7e69..6e30724e1f7 100644 --- a/lib/gitlab/ldap/access.rb +++ b/lib/gitlab/ldap/access.rb @@ -34,7 +34,14 @@ module Gitlab def allowed? if Gitlab::LDAP::Person.find_by_dn(user.ldap_identity.extern_uid, adapter) return true unless ldap_config.active_directory - !Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter) + + # Block user in GitLab if he/she was blocked in AD + if Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter) + user.block unless user.blocked? + false + else + true + end else false end diff --git a/lib/gitlab/ldap/authentication.rb b/lib/gitlab/ldap/authentication.rb index 8af2c74e959..649cf3194b8 100644 --- a/lib/gitlab/ldap/authentication.rb +++ b/lib/gitlab/ldap/authentication.rb @@ -50,7 +50,7 @@ module Gitlab end def user_filter(login) - filter = Net::LDAP::Filter.eq(config.uid, login) + filter = Net::LDAP::Filter.equals(config.uid, login) # Apply LDAP user filter if present if config.user_filter.present? diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb index 3e0b3e6cbf8..3c426179375 100644 --- a/lib/gitlab/ldap/person.rb +++ b/lib/gitlab/ldap/person.rb @@ -9,10 +9,12 @@ module Gitlab attr_accessor :entry, :provider def self.find_by_uid(uid, adapter) + uid = Net::LDAP::Filter.escape(uid) adapter.user(adapter.config.uid, uid) end def self.find_by_dn(dn, adapter) + dn = Net::LDAP::Filter.escape(dn) adapter.user('dn', dn) end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index fb0218a2778..2dfa18da482 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -14,6 +14,7 @@ module Gitlab # * !123 for merge requests # * $123 for snippets # * 123456 for commits + # * 123456...7890123 for commit ranges (comparisons) # # It also parses Emoji codes to insert images. See # http://www.emoji-cheat-sheet.com/ for a list of the supported icons. @@ -121,7 +122,7 @@ module Gitlab text end - NAME_STR = '[a-zA-Z][a-zA-Z0-9_\-\.]*' + NAME_STR = '[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*' PROJ_STR = "(?<project>#{NAME_STR}/#{NAME_STR})" REFERENCE_PATTERN = %r{ @@ -133,13 +134,14 @@ module Gitlab |#{PROJ_STR}?\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID |#{PROJ_STR}?!(?<merge_request>\d+) # MR ID |\$(?<snippet>\d+) # Snippet ID + |(#{PROJ_STR}@)?(?<commit_range>[\h]{6,40}\.{2,3}[\h]{6,40}) # Commit range |(#{PROJ_STR}@)?(?<commit>[\h]{6,40}) # Commit ID |(?<skip>gfm-extraction-[\h]{6,40}) # Skip gfm extractions. Otherwise will be parsed as commit ) (?<suffix>\W)? # Suffix }x.freeze - TYPES = [:user, :issue, :label, :merge_request, :snippet, :commit].freeze + TYPES = [:user, :issue, :label, :merge_request, :snippet, :commit, :commit_range].freeze def parse_references(text, project = @project) # parse reference links @@ -202,7 +204,7 @@ module Gitlab ) if identifier == "all" - link_to("@all", project_url(project), options) + link_to("@all", namespace_project_url(project.namespace, project), options) elsif namespace = Namespace.find_by(path: identifier) url = if namespace.type == "Group" @@ -222,7 +224,7 @@ module Gitlab ) link_to( render_colored_label(label), - project_issues_path(project, label_name: label.name), + namespace_project_issues_path(project.namespace, project, label_name: label.name), options ) end @@ -255,7 +257,8 @@ module Gitlab title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}" ) - url = project_merge_request_url(project, merge_request) + url = namespace_project_merge_request_url(project.namespace, project, + merge_request) link_to("#{prefix_text}!#{identifier}", url, options) end end @@ -266,8 +269,11 @@ module Gitlab title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}" ) - link_to("$#{identifier}", project_snippet_url(project, snippet), - options) + link_to( + "$#{identifier}", + namespace_project_snippet_url(project.namespace, project, snippet), + options + ) end end @@ -280,7 +286,31 @@ module Gitlab prefix_text = "#{prefix_text}@" if prefix_text link_to( "#{prefix_text}#{identifier}", - project_commit_url(project, commit), + namespace_project_commit_url(project.namespace, project, commit), + options + ) + end + end + + def reference_commit_range(identifier, project = @project, prefix_text = nil) + from_id, to_id = identifier.split(/\.{2,3}/, 2) + + inclusive = identifier !~ /\.{3}/ + from_id << "^" if inclusive + + if project.valid_repo? && + from = project.repository.commit(from_id) && + to = project.repository.commit(to_id) + + options = html_options.merge( + title: "Commits #{from_id} through #{to_id}", + class: "gfm gfm-commit_range #{html_options[:class]}" + ) + prefix_text = "#{prefix_text}@" if prefix_text + + link_to( + "#{prefix_text}#{identifier}", + namespace_project_compare_url(project.namespace, project, from: from_id, to: to_id), options ) end diff --git a/lib/gitlab/middleware/static.rb b/lib/gitlab/middleware/static.rb new file mode 100644 index 00000000000..85ffa8aca68 --- /dev/null +++ b/lib/gitlab/middleware/static.rb @@ -0,0 +1,13 @@ +module Gitlab + module Middleware + class Static < ActionDispatch::Static + UPLOADS_REGEX = /\A\/uploads(\/|\z)/.freeze + + def call(env) + return @app.call(env) if env['PATH_INFO'] =~ UPLOADS_REGEX + + super + end + end + end +end diff --git a/lib/gitlab/middleware/timeout.rb b/lib/gitlab/middleware/timeout.rb new file mode 100644 index 00000000000..015600392b9 --- /dev/null +++ b/lib/gitlab/middleware/timeout.rb @@ -0,0 +1,13 @@ +module Gitlab + module Middleware + class Timeout < Rack::Timeout + GRACK_REGEX = /[-\/\w\.]+\.git\//.freeze + + def call(env) + return @app.call(env) if env['PATH_INFO'] =~ GRACK_REGEX + + super + end + end + end +end diff --git a/lib/gitlab/note_data_builder.rb b/lib/gitlab/note_data_builder.rb new file mode 100644 index 00000000000..644dec45dca --- /dev/null +++ b/lib/gitlab/note_data_builder.rb @@ -0,0 +1,77 @@ +module Gitlab + class NoteDataBuilder + class << self + # Produce a hash of post-receive data + # + # For all notes: + # + # data = { + # object_kind: "note", + # user: { + # name: String, + # username: String, + # avatar_url: String + # } + # project_id: Integer, + # repository: { + # name: String, + # url: String, + # description: String, + # homepage: String, + # } + # object_attributes: { + # <hook data for note> + # } + # <note-specific data>: { + # } + # note-specific data is a hash with one of the following keys and contains + # the hook data for that type. + # - commit + # - issue + # - merge_request + # - snippet + # + def build(note, user) + project = note.project + data = build_base_data(project, user, note) + + if note.for_commit? + data[:commit] = build_data_for_commit(project, user, note) + elsif note.for_issue? + data[:issue] = note.noteable.hook_attrs + elsif note.for_merge_request? + data[:merge_request] = note.noteable.hook_attrs + elsif note.for_project_snippet? + data[:snippet] = note.noteable.hook_attrs + end + + data + end + + def build_base_data(project, user, note) + base_data = { + object_kind: "note", + user: user.hook_attrs, + project_id: project.id, + repository: { + name: project.name, + url: project.url_to_repo, + description: project.description, + homepage: project.web_url, + }, + object_attributes: note.hook_attrs + } + + base_data[:object_attributes][:url] = + Gitlab::UrlBuilder.new(:note).build(note.id) + base_data + end + + def build_data_for_commit(project, user, note) + # commit_id is the SHA hash + commit = project.repository.commit(note.commit_id) + commit.hook_attrs(project) + end + end + end +end diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb index 6861427864e..c023d275703 100644 --- a/lib/gitlab/oauth/user.rb +++ b/lib/gitlab/oauth/user.rb @@ -85,11 +85,12 @@ module Gitlab def user_attributes { - name: auth_hash.name, - username: auth_hash.username, - email: auth_hash.email, - password: auth_hash.password, - password_confirmation: auth_hash.password + name: auth_hash.name, + username: ::User.clean_username(auth_hash.username), + email: auth_hash.email, + password: auth_hash.password, + password_confirmation: auth_hash.password, + password_automatically_set: true } end diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index faea6ae375c..0cc6b0ac694 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -9,6 +9,7 @@ module Gitlab # ref: String, # user_id: String, # user_name: String, + # user_email: String # project_id: String, # repository: { # name: String, @@ -29,18 +30,23 @@ module Gitlab # Hash to be passed as post_receive_data data = { + object_kind: "push", before: oldrev, after: newrev, ref: ref, checkout_sha: checkout_sha(project.repository, newrev, ref), user_id: user.id, user_name: user.name, + user_email: user.email, project_id: project.id, repository: { name: project.name, url: project.url_to_repo, description: project.description, homepage: project.web_url, + git_http_url: project.http_url_to_repo, + git_ssh_url: project.ssh_url_to_repo, + visibility_level: project.visibility_level }, commits: [], total_commits_count: commits_count @@ -52,6 +58,7 @@ module Gitlab data[:commits] << commit.hook_attrs(project) end + data[:commits] = "" if data[:commits].count == 0 data end @@ -59,12 +66,13 @@ module Gitlab # existing project and commits to test web hooks def build_sample(project, user) commits = project.repository.commits(project.default_branch, nil, 3) - build(project, user, commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", commits) + ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}" + build(project, user, commits.last.id, commits.first.id, ref, commits) end def checkout_sha(repository, newrev, ref) - if newrev != Gitlab::Git::BLANK_SHA && ref.start_with?('refs/tags/') - tag_name = Gitlab::Git.extract_ref_name(ref) + if newrev != Gitlab::Git::BLANK_SHA && Gitlab::Git.tag_ref?(ref) + tag_name = Gitlab::Git.ref_name(ref) tag = repository.find_tag(tag_name) if tag diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 0b9177afa4f..5b9772de168 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -1,13 +1,13 @@ module Gitlab # Extract possible GFM references from an arbitrary String for further processing. class ReferenceExtractor - attr_accessor :users, :labels, :issues, :merge_requests, :snippets, :commits + attr_accessor :users, :labels, :issues, :merge_requests, :snippets, :commits, :commit_ranges include Markdown def initialize - @users, @labels, @issues, @merge_requests, @snippets, @commits = - [], [], [], [], [], [] + @users, @labels, @issues, @merge_requests, @snippets, @commits, @commit_ranges = + [], [], [], [], [], [], [] end def analyze(string, project) @@ -60,6 +60,16 @@ module Gitlab end.reject(&:nil?) end + def commit_ranges_for(project = nil) + commit_ranges.map do |entry| + repo = entry[:project].repository if entry[:project] + if repo && should_lookup?(project, entry[:project]) + from_id, to_id = entry[:id].split(/\.{2,3}/, 2) + [repo.commit(from_id), repo.commit(to_id)] + end + end.reject(&:nil?) + end + private def reference_link(type, identifier, project, _) @@ -71,7 +81,7 @@ module Gitlab if entry_project.nil? false else - project.nil? || project.id == entry_project.id + project.nil? || entry_project.default_issues_tracker? end end end diff --git a/lib/gitlab/satellite/files/edit_file_action.rb b/lib/gitlab/satellite/files/edit_file_action.rb index 2834b722b27..3cb9c0b5ecb 100644 --- a/lib/gitlab/satellite/files/edit_file_action.rb +++ b/lib/gitlab/satellite/files/edit_file_action.rb @@ -10,12 +10,16 @@ module Gitlab # Returns false if committing the change fails # Returns false if pushing from the satellite to bare repo failed or was rejected # Returns true otherwise - def commit!(content, commit_message, encoding) + def commit!(content, commit_message, encoding, new_branch = nil) in_locked_and_timed_satellite do |repo| prepare_satellite!(repo) # create target branch in satellite at the corresponding commit from bare repo - repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}") + begin + repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}") + rescue Grit::Git::CommandFailed => ex + log_and_raise(CheckoutFailed, ex.message) + end # update the file in the satellite's working dir file_path_in_satellite = File.join(repo.working_dir, file_path) @@ -31,19 +35,33 @@ module Gitlab # commit the changes # will raise CommandFailed when commit fails - repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) + begin + repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) + rescue Grit::Git::CommandFailed => ex + log_and_raise(CommitFailed, ex.message) + end + target_branch = new_branch.present? ? "#{ref}:#{new_branch}" : ref + # push commit back to bare repo # will raise CommandFailed when push fails - repo.git.push({ raise: true, timeout: true }, :origin, ref) + begin + repo.git.push({ raise: true, timeout: true }, :origin, target_branch) + rescue Grit::Git::CommandFailed => ex + log_and_raise(PushFailed, ex.message) + end # everything worked true end - rescue Grit::Git::CommandFailed => ex - Gitlab::GitLogger.error(ex.message) - false + end + + private + + def log_and_raise(errorClass, message) + Gitlab::GitLogger.error(message) + raise(errorClass, message) end end end diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb index 69f7ffa94e4..724dfa0d042 100644 --- a/lib/gitlab/satellite/files/new_file_action.rb +++ b/lib/gitlab/satellite/files/new_file_action.rb @@ -9,7 +9,7 @@ module Gitlab # Returns false if committing the change fails # Returns false if pushing from the satellite to bare repo failed or was rejected # Returns true otherwise - def commit!(content, commit_message, encoding) + def commit!(content, commit_message, encoding, new_branch = nil) in_locked_and_timed_satellite do |repo| prepare_satellite!(repo) @@ -45,9 +45,15 @@ module Gitlab # will raise CommandFailed when commit fails repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) + target_branch = if new_branch.present? && !@project.empty_repo? + "#{ref}:#{new_branch}" + else + "#{current_ref}:#{ref}" + end + # push commit back to bare repo # will raise CommandFailed when push fails - repo.git.push({ raise: true, timeout: true }, :origin, "#{current_ref}:#{ref}") + repo.git.push({ raise: true, timeout: true }, :origin, target_branch) # everything worked true diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index 62d1bb364d3..70125d539da 100644 --- a/lib/gitlab/satellite/satellite.rb +++ b/lib/gitlab/satellite/satellite.rb @@ -1,5 +1,9 @@ module Gitlab module Satellite + class CheckoutFailed < StandardError; end + class CommitFailed < StandardError; end + class PushFailed < StandardError; end + class Satellite include Gitlab::Popen diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb index a7c83a880f6..9799e54de5d 100644 --- a/lib/gitlab/theme.rb +++ b/lib/gitlab/theme.rb @@ -5,6 +5,7 @@ module Gitlab MODERN = 3 unless const_defined?(:MODERN) GRAY = 4 unless const_defined?(:GRAY) COLOR = 5 unless const_defined?(:COLOR) + BLUE = 6 unless const_defined?(:BLUE) def self.css_class_by_id(id) themes = { @@ -12,7 +13,8 @@ module Gitlab MARS => "ui_mars", MODERN => "ui_modern", GRAY => "ui_gray", - COLOR => "ui_color" + COLOR => "ui_color", + BLUE => "ui_blue" } id ||= Gitlab.config.gitlab.default_theme diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index 877488d8471..6830d15875a 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -1,6 +1,7 @@ module Gitlab class UrlBuilder include Rails.application.routes.url_helpers + include GitlabRoutingHelper def initialize(type) @type = type @@ -9,17 +10,51 @@ module Gitlab def build(id) case @type when :issue - issue_url(id) + build_issue_url(id) + when :merge_request + build_merge_request_url(id) + when :note + build_note_url(id) + end end private - def issue_url(id) + def build_issue_url(id) issue = Issue.find(id) - project_issue_url(id: issue.iid, - project_id: issue.project, - host: Gitlab.config.gitlab['url']) + issue_url(issue, host: Gitlab.config.gitlab['url']) + end + + def build_merge_request_url(id) + merge_request = MergeRequest.find(id) + merge_request_url(merge_request, host: Gitlab.config.gitlab['url']) + end + + def build_note_url(id) + note = Note.find(id) + if note.for_commit? + namespace_project_commit_url(namespace_id: note.project.namespace, + id: note.commit_id, + project_id: note.project, + host: Gitlab.config.gitlab['url'], + anchor: "note_#{note.id}") + elsif note.for_issue? + issue = Issue.find(note.noteable_id) + issue_url(issue, + host: Gitlab.config.gitlab['url'], + anchor: "note_#{note.id}") + elsif note.for_merge_request? + merge_request = MergeRequest.find(note.noteable_id) + merge_request_url(merge_request, + host: Gitlab.config.gitlab['url'], + anchor: "note_#{note.id}") + elsif note.for_project_snippet? + snippet = Snippet.find(note.noteable_id) + snippet_url(snippet, + host: Gitlab.config.gitlab['url'], + anchor: "note_#{note.id}") + end end end end diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb index 714261f815c..1cd3933e4b7 100644 --- a/lib/redcarpet/render/gitlab_html.rb +++ b/lib/redcarpet/render/gitlab_html.rb @@ -3,13 +3,20 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML attr_reader :template alias_method :h, :template - def initialize(template, options = {}) + def initialize(template, color_scheme, options = {}) @template = template + @color_scheme = color_scheme @project = @template.instance_variable_get("@project") @options = options.dup super options end + def preprocess(full_document) + # Redcarpet doesn't allow SMB links when `safe_links_only` is enabled. + # FTP links are allowed, so we trick Redcarpet. + full_document.gsub("smb://", "ftp://smb:") + end + # If project has issue number 39, apostrophe will be linked in # regular text to the issue as Redcarpet will convert apostrophe to # #39; @@ -34,7 +41,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML end formatter = Rugments::Formatters::HTML.new( - cssclass: "code highlight white #{lexer.tag}" + cssclass: "code highlight #{@color_scheme} #{lexer.tag}" ) formatter.format(lexer.lex(code)) end @@ -54,6 +61,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML end def postprocess(full_document) + full_document.gsub!("ftp://smb:", "smb://") + full_document.gsub!("’", "'") unless @template.instance_variable_get("@project_wiki") || @project.nil? full_document = h.create_relative_links(full_document) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index c8b769ace8e..62a4276536c 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -1,5 +1,5 @@ ## GitLab -## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller +## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller, DouweM ## ## Lines starting with two hashes (##) are comments with information. ## Lines starting with one hash (#) are configuration parameters that can be uncommented. @@ -56,6 +56,27 @@ server { try_files $uri $uri/index.html $uri.html @gitlab; } + ## We route uploads through GitLab to prevent XSS and enforce access control. + location /uploads/ { + ## If you use HTTPS make sure you disable gzip compression + ## to be safe against BREACH attack. + # gzip off; + + ## https://github.com/gitlabhq/gitlabhq/issues/694 + ## Some requests take more than 30 seconds. + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Frame-Options SAMEORIGIN; + + proxy_pass http://gitlab; + } + ## If a file, which is not found in the root folder is requested, ## then the proxy passes the request to the upsteam (gitlab unicorn). location @gitlab { diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 19af010a9f7..2aefc944698 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -1,5 +1,5 @@ ## GitLab -## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller +## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller, DouweM ## ## Modified from nginx http version ## Modified from http://blog.phusion.nl/2012/04/21/tutorial-setting-up-gitlab-on-debian-6/ @@ -101,6 +101,28 @@ server { try_files $uri $uri/index.html $uri.html @gitlab; } + ## We route uploads through GitLab to prevent XSS and enforce access control. + location /uploads/ { + ## If you use HTTPS make sure you disable gzip compression + ## to be safe against BREACH attack. + gzip off; + + ## https://github.com/gitlabhq/gitlabhq/issues/694 + ## Some requests take more than 30 seconds. + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_redirect off; + + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Frame-Options SAMEORIGIN; + + proxy_pass http://gitlab; + } + ## If a file, which is not found in the root folder is requested, ## then the proxy passes the request to the upsteam (gitlab unicorn). location @gitlab { diff --git a/lib/tasks/brakeman.rake b/lib/tasks/brakeman.rake new file mode 100644 index 00000000000..abcb5f0ae46 --- /dev/null +++ b/lib/tasks/brakeman.rake @@ -0,0 +1,9 @@ +desc 'Security check via brakeman' +task :brakeman do + if system("brakeman --skip-files lib/backup/repository.rb -w3 -z") + exit 0 + else + puts 'Security check failed' + exit 1 + end +end diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 43115915de1..976c4b5f22f 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -29,6 +29,7 @@ namespace :gitlab do check_redis_version check_ruby_version check_git_version + check_active_users finished_checking "GitLab" end @@ -781,6 +782,10 @@ namespace :gitlab do end end + def check_active_users + puts "Active users: #{User.active.count}" + end + def omnibus_gitlab? Dir.pwd == '/opt/gitlab/embedded/service/gitlab-rails' end diff --git a/lib/tasks/test.rake b/lib/tasks/test.rake index 3ea9290a814..a39d9649876 100644 --- a/lib/tasks/test.rake +++ b/lib/tasks/test.rake @@ -9,5 +9,5 @@ unless Rails.env.production? require 'coveralls/rake/task' Coveralls::RakeTask.new desc "GITLAB | Run all tests on CI with simplecov" - task :test_ci => [:rubocop, :spinach, :spec, 'coveralls:push'] + task :test_ci => [:rubocop, :brakeman, 'jasmine:ci', :spinach, :spec, 'coveralls:push'] end diff --git a/public/503.html b/public/503.html new file mode 100644 index 00000000000..efdae0f512d --- /dev/null +++ b/public/503.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<head> + <title>Page took too long to load (503)</title> + <link href="/static.css" media="screen" rel="stylesheet" type="text/css" /> +</head> +<body> + <h1>503</h1> + <h3>Page took too long to load.</h3> + <hr/> + <p>Please contact your GitLab administrator if this problem persists.</p> +</body> +</html> diff --git a/safe/public.pem b/safe/public.pem new file mode 100644 index 00000000000..c5ffe20a5c7 --- /dev/null +++ b/safe/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnp2mUaLBoHFX127ysonX +OihiGpI4098eFfH1iAxpKHIof0vs0jFF05IUScNXJZ1U3w8G1U/unY/wGGa3NzAb +ZfDd22eOF6X2Gfiey6U4w9dFf0/UT5x1bphlpX357yh4O9oWWuNaWD062DTbOOsJ +U6UW2U/sZAu/QScys0Nw+gJ58t93hb4jFq+nO5IAQc6g4S8ek5YvIXOshFEpF2in +ZLbSYowx92+9GzfjvdQ7fk0Q2ssg0zfScVa6FY8n019osz0SC3wcSd/qicdfecpu +7oycpd9YDqk4lufE1qVMOsgE8OO4KXMrByz2f+T0p/bH9zdBa5HYylf1T7i60hIL +kQIDAQAB +-----END PUBLIC KEY----- diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index cc32805f5ec..186239d3096 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -7,26 +7,26 @@ describe ApplicationController do it 'should redirect if the user is over their password expiry' do user.password_expires_at = Time.new(2002) - user.ldap_user?.should be_false - controller.stub(:current_user).and_return(user) - controller.should_receive(:redirect_to) - controller.should_receive(:new_profile_password_path) + expect(user.ldap_user?).to be_falsey + allow(controller).to receive(:current_user).and_return(user) + expect(controller).to receive(:redirect_to) + expect(controller).to receive(:new_profile_password_path) controller.send(:check_password_expiration) end it 'should not redirect if the user is under their password expiry' do user.password_expires_at = Time.now + 20010101 - user.ldap_user?.should be_false - controller.stub(:current_user).and_return(user) - controller.should_not_receive(:redirect_to) + expect(user.ldap_user?).to be_falsey + allow(controller).to receive(:current_user).and_return(user) + expect(controller).not_to receive(:redirect_to) controller.send(:check_password_expiration) end it 'should not redirect if the user is over their password expiry but they are an ldap user' do user.password_expires_at = Time.new(2002) - user.stub(:ldap_user?).and_return(true) - controller.stub(:current_user).and_return(user) - controller.should_not_receive(:redirect_to) + allow(user).to receive(:ldap_user?).and_return(true) + allow(controller).to receive(:current_user).and_return(user) + expect(controller).not_to receive(:redirect_to) controller.send(:check_password_expiration) end end diff --git a/spec/controllers/blob_controller_spec.rb b/spec/controllers/blob_controller_spec.rb index 11d748ca77f..a1102f28340 100644 --- a/spec/controllers/blob_controller_spec.rb +++ b/spec/controllers/blob_controller_spec.rb @@ -9,29 +9,32 @@ describe Projects::BlobController do project.team << [user, :master] - project.stub(:branches).and_return(['master', 'foo/bar/baz']) - project.stub(:tags).and_return(['v1.0.0', 'v2.0.0']) + allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz']) + allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0']) controller.instance_variable_set(:@project, project) end describe "GET show" do render_views - before { get :show, project_id: project.to_param, id: id } + before do + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: id) + end context "valid branch, valid file" do let(:id) { 'master/README.md' } - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end context "valid branch, invalid file" do let(:id) { 'master/invalid-path.rb' } - it { should respond_with(:not_found) } + it { is_expected.to respond_with(:not_found) } end context "invalid branch, valid file" do let(:id) { 'invalid-branch/README.md' } - it { should respond_with(:not_found) } + it { is_expected.to respond_with(:not_found) } end end @@ -39,13 +42,17 @@ describe Projects::BlobController do render_views before do - get :show, project_id: project.to_param, id: id + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: id) controller.instance_variable_set(:@blob, nil) end context 'redirect to tree' do let(:id) { 'markdown/doc' } - it { should redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") } + it 'redirects' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") + end end end end diff --git a/spec/controllers/branches_controller_spec.rb b/spec/controllers/branches_controller_spec.rb index 610d7a84e31..51397382cfb 100644 --- a/spec/controllers/branches_controller_spec.rb +++ b/spec/controllers/branches_controller_spec.rb @@ -9,8 +9,8 @@ describe Projects::BranchesController do project.team << [user, :master] - project.stub(:branches).and_return(['master', 'foo/bar/baz']) - project.stub(:tags).and_return(['v1.0.0', 'v2.0.0']) + allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz']) + allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0']) controller.instance_variable_set(:@project, project) end @@ -19,6 +19,7 @@ describe Projects::BranchesController do before { post :create, + namespace_id: project.namespace.to_param, project_id: project.to_param, branch_name: branch, ref: ref @@ -27,25 +28,31 @@ describe Projects::BranchesController do context "valid branch name, valid source" do let(:branch) { "merge_branch" } let(:ref) { "master" } - it { should redirect_to("/#{project.path_with_namespace}/tree/merge_branch") } + it 'redirects' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/tree/merge_branch") + end end context "invalid branch name, valid ref" do let(:branch) { "<script>alert('merge');</script>" } let(:ref) { "master" } - it { should redirect_to("/#{project.path_with_namespace}/tree/alert('merge');") } + it 'redirects' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');") + end end context "valid branch name, invalid ref" do let(:branch) { "merge_branch" } let(:ref) { "<script>alert('ref');</script>" } - it { should render_template("new") } + it { is_expected.to render_template('new') } end context "invalid branch name, invalid ref" do let(:branch) { "<script>alert('merge');</script>" } let(:ref) { "<script>alert('ref');</script>" } - it { should render_template("new") } + it { is_expected.to render_template('new') } end end end diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index cd8b46d7672..3394a1f863f 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -13,27 +13,32 @@ describe Projects::CommitController do describe "#show" do shared_examples "export as" do |format| it "should generally work" do - get :show, project_id: project.to_param, id: commit.id, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id, format: format) expect(response).to be_success end it "should generate it" do - Commit.any_instance.should_receive(:"to_#{format}") + expect_any_instance_of(Commit).to receive(:"to_#{format}") - get :show, project_id: project.to_param, id: commit.id, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id, format: format) end it "should render it" do - get :show, project_id: project.to_param, id: commit.id, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id, format: format) expect(response.body).to eq(commit.send(:"to_#{format}")) end it "should not escape Html" do - Commit.any_instance.stub(:"to_#{format}").and_return('HTML entities &<>" ') + allow_any_instance_of(Commit).to receive(:"to_#{format}"). + and_return('HTML entities &<>" ') - get :show, project_id: project.to_param, id: commit.id, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id, format: format) expect(response.body).to_not include('&') expect(response.body).to_not include('>') @@ -47,7 +52,8 @@ describe Projects::CommitController do let(:format) { :diff } it "should really only be a git diff" do - get :show, project_id: project.to_param, id: commit.id, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id, format: format) expect(response.body).to start_with("diff --git") end @@ -58,13 +64,15 @@ describe Projects::CommitController do let(:format) { :patch } it "should really be a git email patch" do - get :show, project_id: project.to_param, id: commit.id, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id, format: format) expect(response.body).to start_with("From #{commit.id}") end it "should contain a git diff" do - get :show, project_id: project.to_param, id: commit.id, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id, format: format) expect(response.body).to match(/^diff --git/) end @@ -73,7 +81,8 @@ describe Projects::CommitController do describe "#branches" do it "contains branch and tags information" do - get :branches, project_id: project.to_param, id: commit.id + get(:branches, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: commit.id) expect(assigns(:branches)).to include("master", "feature_conflict") expect(assigns(:tags)).to include("v1.1.0") diff --git a/spec/controllers/commits_controller_spec.rb b/spec/controllers/commits_controller_spec.rb index 0c19d755eb1..2184b35152e 100644 --- a/spec/controllers/commits_controller_spec.rb +++ b/spec/controllers/commits_controller_spec.rb @@ -12,9 +12,10 @@ describe Projects::CommitsController do describe "GET show" do context "as atom feed" do it "should render as atom" do - get :show, project_id: project.to_param, id: "master", format: "atom" - response.should be_success - response.content_type.should == 'application/atom+xml' + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: "master", format: "atom") + expect(response).to be_success + expect(response.content_type).to eq('application/atom+xml') end end end diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb new file mode 100644 index 00000000000..5dd4124061c --- /dev/null +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +describe Import::BitbucketController do + let(:user) { create(:user, bitbucket_access_token: 'asd123', bitbucket_access_token_secret: "sekret") } + + before do + sign_in(user) + controller.stub(:bitbucket_import_enabled?).and_return(true) + end + + describe "GET callback" do + before do + session[:oauth_request_token] = {} + end + + it "updates access token" do + token = "asdasd12345" + secret = "sekrettt" + access_token = double(token: token, secret: secret) + Gitlab::BitbucketImport::Client.any_instance.stub(:get_token).and_return(access_token) + Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket") + + get :callback + + expect(user.reload.bitbucket_access_token).to eq(token) + expect(user.reload.bitbucket_access_token_secret).to eq(secret) + expect(controller).to redirect_to(status_import_bitbucket_url) + end + end + + describe "GET status" do + before do + @repo = OpenStruct.new(slug: 'vim', owner: 'asd') + end + + it "assigns variables" do + @project = create(:project, import_type: 'bitbucket', creator_id: user.id) + controller.stub_chain(:client, :projects).and_return([@repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([@project]) + expect(assigns(:repos)).to eq([@repo]) + end + + it "does not show already added project" do + @project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim') + controller.stub_chain(:client, :projects).and_return([@repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([@project]) + expect(assigns(:repos)).to eq([]) + end + end + + describe "POST create" do + before do + @repo = { + slug: 'vim', + owner: "john" + }.with_indifferent_access + end + + it "takes already existing namespace" do + namespace = create(:namespace, name: "john", owner: user) + expect(Gitlab::BitbucketImport::KeyAdder). + to receive(:new).with(@repo, user). + and_return(double(execute: true)) + expect(Gitlab::BitbucketImport::ProjectCreator). + to receive(:new).with(@repo, namespace, user). + and_return(double(execute: true)) + controller.stub_chain(:client, :project).and_return(@repo) + + post :create, format: :js + end + end +end diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index f80b3884d88..5b967bfcc0c 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -5,35 +5,42 @@ describe Import::GithubController do before do sign_in(user) + controller.stub(:github_import_enabled?).and_return(true) end describe "GET callback" do it "updates access token" do token = "asdasd12345" - Gitlab::GithubImport::Client.any_instance.stub(:get_token).and_return(token) - Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "github") + allow_any_instance_of(Gitlab::GithubImport::Client). + to receive(:get_token).and_return(token) + Gitlab.config.omniauth.providers << OpenStruct.new(app_id: 'asd123', + app_secret: 'asd123', + name: 'github') get :callback - - user.reload.github_access_token.should == token - controller.should redirect_to(status_import_github_url) + + expect(user.reload.github_access_token).to eq(token) + expect(controller).to redirect_to(status_import_github_url) end end describe "GET status" do before do @repo = OpenStruct.new(login: 'vim', full_name: 'asd/vim') + @org = OpenStruct.new(login: 'company') + @org_repo = OpenStruct.new(login: 'company', full_name: 'company/repo') end it "assigns variables" do @project = create(:project, import_type: 'github', creator_id: user.id) controller.stub_chain(:client, :repos).and_return([@repo]) - controller.stub_chain(:client, :orgs).and_return([]) + controller.stub_chain(:client, :orgs).and_return([@org]) + controller.stub_chain(:client, :org_repos).with(@org.login).and_return([@org_repo]) get :status expect(assigns(:already_added_projects)).to eq([@project]) - expect(assigns(:repos)).to eq([@repo]) + expect(assigns(:repos)).to eq([@repo, @org_repo]) end it "does not show already added project" do @@ -55,7 +62,8 @@ describe Import::GithubController do it "takes already existing namespace" do namespace = create(:namespace, name: "john", owner: user) - Gitlab::GithubImport::ProjectCreator.should_receive(:new).with(@repo, namespace, user). + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(@repo, namespace, user). and_return(double(execute: true)) controller.stub_chain(:client, :repo).and_return(@repo) diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb index 36995091c69..b6b86b1bcee 100644 --- a/spec/controllers/import/gitlab_controller_spec.rb +++ b/spec/controllers/import/gitlab_controller_spec.rb @@ -5,6 +5,7 @@ describe Import::GitlabController do before do sign_in(user) + controller.stub(:gitlab_import_enabled?).and_return(true) end describe "GET callback" do @@ -14,9 +15,9 @@ describe Import::GitlabController do Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "gitlab") get :callback - - user.reload.gitlab_access_token.should == token - controller.should redirect_to(status_import_gitlab_url) + + expect(user.reload.gitlab_access_token).to eq(token) + expect(controller).to redirect_to(status_import_gitlab_url) end end @@ -28,7 +29,7 @@ describe Import::GitlabController do it "assigns variables" do @project = create(:project, import_type: 'gitlab', creator_id: user.id) controller.stub_chain(:client, :projects).and_return([@repo]) - + get :status expect(assigns(:already_added_projects)).to eq([@project]) @@ -38,7 +39,7 @@ describe Import::GitlabController do it "does not show already added project" do @project = create(:project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim') controller.stub_chain(:client, :projects).and_return([@repo]) - + get :status expect(assigns(:already_added_projects)).to eq([@project]) @@ -58,7 +59,8 @@ describe Import::GitlabController do it "takes already existing namespace" do namespace = create(:namespace, name: "john", owner: user) - Gitlab::GitlabImport::ProjectCreator.should_receive(:new).with(@repo, namespace, user). + expect(Gitlab::GitlabImport::ProjectCreator). + to receive(:new).with(@repo, namespace, user). and_return(double(execute: true)) controller.stub_chain(:client, :project).and_return(@repo) diff --git a/spec/controllers/import/gitorious_controller_spec.rb b/spec/controllers/import/gitorious_controller_spec.rb new file mode 100644 index 00000000000..07c9484bf1a --- /dev/null +++ b/spec/controllers/import/gitorious_controller_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Import::GitoriousController do + let(:user) { create(:user) } + + before do + sign_in(user) + end + + describe "GET new" do + it "redirects to import endpoint on gitorious.org" do + get :new + + expect(controller).to redirect_to("https://gitorious.org/gitlab-import?callback_url=http://test.host/import/gitorious/callback") + end + end + + describe "GET callback" do + it "stores repo list in session" do + get :callback, repos: 'foo/bar,baz/qux' + + expect(session[:gitorious_repos]).to eq('foo/bar,baz/qux') + end + end + + describe "GET status" do + before do + @repo = OpenStruct.new(full_name: 'asd/vim') + end + + it "assigns variables" do + @project = create(:project, import_type: 'gitorious', creator_id: user.id) + controller.stub_chain(:client, :repos).and_return([@repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([@project]) + expect(assigns(:repos)).to eq([@repo]) + end + + it "does not show already added project" do + @project = create(:project, import_type: 'gitorious', creator_id: user.id, import_source: 'asd/vim') + controller.stub_chain(:client, :repos).and_return([@repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([@project]) + expect(assigns(:repos)).to eq([]) + end + end + + describe "POST create" do + before do + @repo = Gitlab::GitoriousImport::Repository.new('asd/vim') + end + + it "takes already existing namespace" do + namespace = create(:namespace, name: "asd", owner: user) + expect(Gitlab::GitoriousImport::ProjectCreator). + to receive(:new).with(@repo, namespace, user). + and_return(double(execute: true)) + controller.stub_chain(:client, :repo).and_return(@repo) + + post :create, format: :js + end + end +end diff --git a/spec/controllers/merge_requests_controller_spec.rb b/spec/controllers/merge_requests_controller_spec.rb index 300527e4ff2..d6f56ed33d6 100644 --- a/spec/controllers/merge_requests_controller_spec.rb +++ b/spec/controllers/merge_requests_controller_spec.rb @@ -13,27 +13,32 @@ describe Projects::MergeRequestsController do describe "#show" do shared_examples "export merge as" do |format| it "should generally work" do - get :show, project_id: project.to_param, id: merge_request.iid, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: merge_request.iid, format: format) expect(response).to be_success end it "should generate it" do - MergeRequest.any_instance.should_receive(:"to_#{format}") + expect_any_instance_of(MergeRequest).to receive(:"to_#{format}") - get :show, project_id: project.to_param, id: merge_request.iid, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: merge_request.iid, format: format) end it "should render it" do - get :show, project_id: project.to_param, id: merge_request.iid, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: merge_request.iid, format: format) expect(response.body).to eq((merge_request.send(:"to_#{format}",user)).to_s) end it "should not escape Html" do - MergeRequest.any_instance.stub(:"to_#{format}").and_return('HTML entities &<>" ') + allow_any_instance_of(MergeRequest).to receive(:"to_#{format}"). + and_return('HTML entities &<>" ') - get :show, project_id: project.to_param, id: merge_request.iid, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: merge_request.iid, format: format) expect(response.body).to_not include('&') expect(response.body).to_not include('>') @@ -47,7 +52,8 @@ describe Projects::MergeRequestsController do let(:format) { :diff } it "should really only be a git diff" do - get :show, project_id: project.to_param, id: merge_request.iid, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: merge_request.iid, format: format) expect(response.body).to start_with("diff --git") end @@ -58,13 +64,15 @@ describe Projects::MergeRequestsController do let(:format) { :patch } it "should really be a git email patch with commit" do - get :show, project_id: project.to_param, id: merge_request.iid, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: merge_request.iid, format: format) expect(response.body[0..100]).to start_with("From #{merge_request.commits.last.id}") end it "should contain git diffs" do - get :show, project_id: project.to_param, id: merge_request.iid, format: format + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: merge_request.iid, format: format) expect(response.body).to match(/^diff --git/) end diff --git a/spec/controllers/projects/protected_branches_controller_spec.rb b/spec/controllers/projects/protected_branches_controller_spec.rb new file mode 100644 index 00000000000..596d8d34b7c --- /dev/null +++ b/spec/controllers/projects/protected_branches_controller_spec.rb @@ -0,0 +1,10 @@ +require('spec_helper') + +describe Projects::ProtectedBranchesController do + describe "GET #index" do + let(:project) { create(:project_empty_repo, :public) } + it "redirect empty repo to projects page" do + get(:index, namespace_id: project.namespace.to_param, project_id: project.to_param) + end + end +end diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb new file mode 100644 index 00000000000..029f48b2d7a --- /dev/null +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -0,0 +1,57 @@ +require('spec_helper') + +describe Projects::UploadsController do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + + describe "POST #create" do + before do + sign_in(user) + project.team << [user, :developer] + end + + context "without params['file']" do + it "returns an error" do + post :create, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + format: :json + expect(response.status).to eq(422) + end + end + + context 'with valid image' do + before do + post :create, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + file: jpg, + format: :json + end + + it 'returns a content with original filename, new link, and correct type.' do + expect(response.body).to match '\"alt\":\"rails_sample\"' + expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads" + expect(response.body).to match '\"is_image\":true' + end + end + + context 'with valid non-image file' do + before do + post :create, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + file: txt, + format: :json + end + + it 'returns a content with original filename, new link, and correct type.' do + expect(response.body).to match '\"alt\":\"doc_sample.txt\"' + expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads" + expect(response.body).to match '\"is_image\":false' + end + end + end +end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 71bc49787cc..a1b82a32150 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -7,56 +7,41 @@ describe ProjectsController do let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } - describe "POST #upload_image" do - before do - sign_in(user) - project.team << [user, :developer] - end + describe "GET show" do - context "without params['markdown_img']" do - it "returns an error" do - post :upload_image, id: project.to_param, format: :json - expect(response.status).to eq(422) - end - end + context "when requested by `go get`" do + render_views - context "with invalid file" do - before do - post :upload_image, id: project.to_param, markdown_img: txt, format: :json - end + it "renders the go-import meta tag" do + get :show, "go-get" => "1", namespace_id: "bogus_namespace", id: "bogus_project" - it "returns an error" do - expect(response.status).to eq(422) - end - end - - context "with valid file" do - before do - post :upload_image, id: project.to_param, markdown_img: jpg, format: :json - end - - it "returns a content with original filename and new link." do - expect(response.body).to match "\"alt\":\"rails_sample\"" - expect(response.body).to match "\"url\":\"http://test.host/uploads/#{project.path_with_namespace}" + expect(response.body).to include("name='go-import'") + + content = "localhost/bogus_namespace/bogus_project git http://localhost/bogus_namespace/bogus_project.git" + expect(response.body).to include("content='#{content}'") end end end - + describe "POST #toggle_star" do it "toggles star if user is signed in" do sign_in(user) - expect(user.starred?(public_project)).to be_false - post :toggle_star, id: public_project.to_param - expect(user.starred?(public_project)).to be_true - post :toggle_star, id: public_project.to_param - expect(user.starred?(public_project)).to be_false + expect(user.starred?(public_project)).to be_falsey + post(:toggle_star, namespace_id: public_project.namespace.to_param, + id: public_project.to_param) + expect(user.starred?(public_project)).to be_truthy + post(:toggle_star, namespace_id: public_project.namespace.to_param, + id: public_project.to_param) + expect(user.starred?(public_project)).to be_falsey end it "does nothing if user is not signed in" do - post :toggle_star, id: public_project.to_param - expect(user.starred?(public_project)).to be_false - post :toggle_star, id: public_project.to_param - expect(user.starred?(public_project)).to be_false + post(:toggle_star, namespace_id: project.namespace.to_param, + id: public_project.to_param) + expect(user.starred?(public_project)).to be_falsey + post(:toggle_star, namespace_id: project.namespace.to_param, + id: public_project.to_param) + expect(user.starred?(public_project)).to be_falsey end end end diff --git a/spec/controllers/tree_controller_spec.rb b/spec/controllers/tree_controller_spec.rb index 8147fb0e6fb..7b219819bbc 100644 --- a/spec/controllers/tree_controller_spec.rb +++ b/spec/controllers/tree_controller_spec.rb @@ -9,8 +9,8 @@ describe Projects::TreeController do project.team << [user, :master] - project.stub(:branches).and_return(['master', 'foo/bar/baz']) - project.stub(:tags).and_return(['v1.0.0', 'v2.0.0']) + allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz']) + allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0']) controller.instance_variable_set(:@project, project) end @@ -18,26 +18,29 @@ describe Projects::TreeController do # Make sure any errors accessing the tree in our views bubble up to this spec render_views - before { get :show, project_id: project.to_param, id: id } + before do + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: id) + end context "valid branch, no path" do let(:id) { 'master' } - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end context "valid branch, valid path" do let(:id) { 'master/encoding/' } - it { should respond_with(:success) } + it { is_expected.to respond_with(:success) } end context "valid branch, invalid path" do let(:id) { 'master/invalid-path/' } - it { should respond_with(:not_found) } + it { is_expected.to respond_with(:not_found) } end context "invalid branch, valid path" do let(:id) { 'invalid-branch/encoding/' } - it { should respond_with(:not_found) } + it { is_expected.to respond_with(:not_found) } end end @@ -45,12 +48,17 @@ describe Projects::TreeController do render_views before do - get :show, project_id: project.to_param, id: id + get(:show, namespace_id: project.namespace.to_param, + project_id: project.to_param, id: id) end context 'redirect to blob' do let(:id) { 'master/README.md' } - it { should redirect_to("/#{project.path_with_namespace}/blob/master/README.md") } + it 'redirects' do + redirect_url = "/#{project.path_with_namespace}/blob/master/README.md" + expect(subject). + to redirect_to(redirect_url) + end end end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb new file mode 100644 index 00000000000..0f9780356b1 --- /dev/null +++ b/spec/controllers/uploads_controller_spec.rb @@ -0,0 +1,296 @@ +require 'spec_helper' + +describe UploadsController do + let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + + describe "GET show" do + context "when viewing a user avatar" do + context "when signed in" do + before do + sign_in(user) + end + + context "when the user is blocked" do + before do + user.block + end + + it "redirects to the sign in page" do + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when viewing a project avatar" do + let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + + context "when the project is public" do + before do + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + it "responds with status 200" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the project is private" do + before do + project.update_attribute(:visibility_level, Project::PRIVATE) + end + + context "when not signed in" do + it "redirects to the sign in page" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when the user has access to the project" do + before do + project.team << [user, :master] + end + + context "when the user is blocked" do + before do + user.block + project.team << [user, :master] + end + + it "redirects to the sign in page" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the user doesn't have access to the project" do + it "responds with status 404" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(404) + end + end + end + end + end + + context "when viewing a group avatar" do + let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:project) { create(:project, namespace: group) } + + context "when the group has public projects" do + before do + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + it "responds with status 200" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the project doesn't have public projects" do + context "when not signed in" do + it "redirects to the sign in page" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when the user has access to the project" do + before do + project.team << [user, :master] + end + + context "when the user is blocked" do + before do + user.block + project.team << [user, :master] + end + + it "redirects to the sign in page" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the user doesn't have access to the project" do + it "responds with status 404" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(404) + end + end + end + end + end + + context "when viewing a note attachment" do + let!(:note) { create(:note, :with_attachment) } + let(:project) { note.project } + + context "when the project is public" do + before do + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + it "responds with status 200" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the project is private" do + before do + project.update_attribute(:visibility_level, Project::PRIVATE) + end + + context "when not signed in" do + it "redirects to the sign in page" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when the user has access to the project" do + before do + project.team << [user, :master] + end + + context "when the user is blocked" do + before do + user.block + project.team << [user, :master] + end + + it "redirects to the sign in page" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the user doesn't have access to the project" do + it "responds with status 404" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(404) + end + end + end + end + end + end +end diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 6ce1d7446fe..77cd37c22d9 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -40,7 +40,7 @@ FactoryGirl.define do source_branch "master" target_branch "feature" - merge_status :can_be_merged + merge_status "can_be_merged" trait :with_diffs do end diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 83d0cc62dbf..f1c33461b55 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -30,6 +30,7 @@ FactoryGirl.define do factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note] factory :note_on_merge_request, traits: [:on_merge_request] factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff] + factory :note_on_project_snippet, traits: [:on_project_snippet] trait :on_commit do project factory: :project @@ -52,6 +53,11 @@ FactoryGirl.define do noteable_type "Issue" end + trait :on_project_snippet do + noteable_id 1 + noteable_type "Snippet" + end + trait :with_attachment do attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") } end diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index 66bef0761c7..c8e218d4d03 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -9,7 +9,7 @@ FactoryGirl.factories.map(&:name).each do |factory_name| next if INVALID_FACTORIES.include?(factory_name) describe "#{factory_name} factory" do it 'should be valid' do - build(factory_name).should be_valid + expect(build(factory_name)).to be_valid end end end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index 37d6b416d22..25862614d28 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -15,12 +15,12 @@ describe "Admin::Hooks", feature: true do within ".sidebar-wrapper" do click_on "Hooks" end - current_path.should == admin_hooks_path + expect(current_path).to eq(admin_hooks_path) end it "should have hooks list" do visit admin_hooks_path - page.should have_content(@system_hook.url) + expect(page).to have_content(@system_hook.url) end end @@ -33,8 +33,8 @@ describe "Admin::Hooks", feature: true do end it "should open new hook popup" do - current_path.should == admin_hooks_path - page.should have_content(@url) + expect(current_path).to eq(admin_hooks_path) + expect(page).to have_content(@url) end end @@ -45,7 +45,7 @@ describe "Admin::Hooks", feature: true do click_link "Test Hook" end - it { current_path.should == admin_hooks_path } + it { expect(current_path).to eq(admin_hooks_path) } end end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 3b3d027ab75..101d955d693 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -8,27 +8,27 @@ describe "Admin::Projects", feature: true do describe "GET /admin/projects" do before do - visit admin_projects_path + visit admin_namespaces_projects_path end it "should be ok" do - current_path.should == admin_projects_path + expect(current_path).to eq(admin_namespaces_projects_path) end it "should have projects list" do - page.should have_content(@project.name) + expect(page).to have_content(@project.name) end end describe "GET /admin/projects/:id" do before do - visit admin_projects_path + visit admin_namespaces_projects_path click_link "#{@project.name}" end it "should have project info" do - page.should have_content(@project.path) - page.should have_content(@project.name) + expect(page).to have_content(@project.path) + expect(page).to have_content(@project.name) end end end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 59c4ffb5624..f97b69713ce 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -9,12 +9,12 @@ describe "Admin::Users", feature: true do end it "should be ok" do - current_path.should == admin_users_path + expect(current_path).to eq(admin_users_path) end it "should have users list" do - page.should have_content(@user.email) - page.should have_content(@user.name) + expect(page).to have_content(@user.email) + expect(page).to have_content(@user.name) end end @@ -33,19 +33,21 @@ describe "Admin::Users", feature: true do it "should apply defaults to user" do click_button "Create user" user = User.find_by(username: 'bang') - user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit - user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group + expect(user.projects_limit). + to eq(Gitlab.config.gitlab.default_projects_limit) + expect(user.can_create_group). + to eq(Gitlab.config.gitlab.default_can_create_group) end it "should create user with valid data" do click_button "Create user" user = User.find_by(username: 'bang') - user.name.should == "Big Bang" - user.email.should == "bigbang@mail.com" + expect(user.name).to eq('Big Bang') + expect(user.email).to eq('bigbang@mail.com') end it "should call send mail" do - Notify.should_receive(:new_user_email) + expect(Notify).to receive(:new_user_email) click_button "Create user" end @@ -54,9 +56,9 @@ describe "Admin::Users", feature: true do click_button "Create user" user = User.find_by(username: 'bang') email = ActionMailer::Base.deliveries.last - email.subject.should have_content("Account was created") - email.text_part.body.should have_content(user.email) - email.text_part.body.should have_content('password') + expect(email.subject).to have_content('Account was created') + expect(email.text_part.body).to have_content(user.email) + expect(email.text_part.body).to have_content('password') end end @@ -67,8 +69,8 @@ describe "Admin::Users", feature: true do end it "should have user info" do - page.should have_content(@user.email) - page.should have_content(@user.name) + expect(page).to have_content(@user.email) + expect(page).to have_content(@user.name) end end @@ -80,8 +82,8 @@ describe "Admin::Users", feature: true do end it "should have user edit page" do - page.should have_content("Name") - page.should have_content("Password") + expect(page).to have_content('Name') + expect(page).to have_content('Password') end describe "Update user" do @@ -93,14 +95,14 @@ describe "Admin::Users", feature: true do end it "should show page with new data" do - page.should have_content("bigbang@mail.com") - page.should have_content("Big Bang") + expect(page).to have_content('bigbang@mail.com') + expect(page).to have_content('Big Bang') end it "should change user entry" do @simple_user.reload - @simple_user.name.should == "Big Bang" - @simple_user.is_admin?.should be_true + expect(@simple_user.name).to eq('Big Bang') + expect(@simple_user.is_admin?).to be_truthy end end end diff --git a/spec/features/admin/security_spec.rb b/spec/features/admin/security_spec.rb index 21b0d8b965e..175fa9d4647 100644 --- a/spec/features/admin/security_spec.rb +++ b/spec/features/admin/security_spec.rb @@ -2,26 +2,26 @@ require 'spec_helper' describe "Admin::Projects", feature: true do describe "GET /admin/projects" do - subject { admin_projects_path } + subject { admin_namespaces_projects_path } - it { should be_allowed_for :admin } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /admin/users" do subject { admin_users_path } - it { should be_allowed_for :admin } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /admin/hooks" do subject { admin_hooks_path } - it { should be_allowed_for :admin } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb index 187f2ffcffd..b710cb3c72f 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/features/atom/dashboard_issues_spec.rb @@ -17,12 +17,13 @@ describe "Dashboard Issues Feed", feature: true do it "should render atom feed via private token" do visit issues_dashboard_path(:atom, private_token: user.private_token) - response_headers['Content-Type'].should have_content("application/atom+xml") - body.should have_selector("title", text: "#{user.name} issues") - body.should have_selector("author email", text: issue1.author_email) - body.should have_selector("entry summary", text: issue1.title) - body.should have_selector("author email", text: issue2.author_email) - body.should have_selector("entry summary", text: issue2.title) + expect(response_headers['Content-Type']). + to have_content('application/atom+xml') + expect(body).to have_selector('title', text: "#{user.name} issues") + expect(body).to have_selector('author email', text: issue1.author_email) + expect(body).to have_selector('entry summary', text: issue1.title) + expect(body).to have_selector('author email', text: issue2.author_email) + expect(body).to have_selector('entry summary', text: issue2.title) end end end diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb index 52ade3e2d31..ad157d742ff 100644 --- a/spec/features/atom/dashboard_spec.rb +++ b/spec/features/atom/dashboard_spec.rb @@ -7,7 +7,7 @@ describe "Dashboard Feed", feature: true do context "projects atom feed via private token" do it "should render projects atom feed" do visit dashboard_path(:atom, private_token: user.private_token) - body.should have_selector("feed title") + expect(body).to have_selector('feed title') end end @@ -24,11 +24,12 @@ describe "Dashboard Feed", feature: true do end it "should have issue opened event" do - body.should have_content("#{user.name} opened issue ##{issue.iid}") + expect(body).to have_content("#{user.name} opened issue ##{issue.iid}") end it "should have issue comment event" do - body.should have_content("#{user.name} commented on issue ##{issue.iid}") + expect(body). + to have_content("#{user.name} commented on issue ##{issue.iid}") end end end diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb index 453dca69094..baa7814e96a 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/features/atom/issues_spec.rb @@ -1,33 +1,36 @@ require 'spec_helper' -describe "Issues Feed", feature: true do - describe "GET /issues" do +describe 'Issues Feed', feature: true do + describe 'GET /issues' do let!(:user) { create(:user) } let!(:project) { create(:project) } let!(:issue) { create(:issue, author: user, project: project) } before { project.team << [user, :developer] } - context "when authenticated" do - it "should render atom feed" do + context 'when authenticated' do + it 'should render atom feed' do login_with user - visit project_issues_path(project, :atom) + visit namespace_project_issues_path(project.namespace, project, :atom) - response_headers['Content-Type'].should have_content("application/atom+xml") - body.should have_selector("title", text: "#{project.name} issues") - body.should have_selector("author email", text: issue.author_email) - body.should have_selector("entry summary", text: issue.title) + expect(response_headers['Content-Type']). + to have_content('application/atom+xml') + expect(body).to have_selector('title', text: "#{project.name} issues") + expect(body).to have_selector('author email', text: issue.author_email) + expect(body).to have_selector('entry summary', text: issue.title) end end - context "when authenticated via private token" do - it "should render atom feed" do - visit project_issues_path(project, :atom, private_token: user.private_token) + context 'when authenticated via private token' do + it 'should render atom feed' do + visit namespace_project_issues_path(project.namespace, project, :atom, + private_token: user.private_token) - response_headers['Content-Type'].should have_content("application/atom+xml") - body.should have_selector("title", text: "#{project.name} issues") - body.should have_selector("author email", text: issue.author_email) - body.should have_selector("entry summary", text: issue.title) + expect(response_headers['Content-Type']). + to have_content('application/atom+xml') + expect(body).to have_selector('title', text: "#{project.name} issues") + expect(body).to have_selector('author email', text: issue.author_email) + expect(body).to have_selector('entry summary', text: issue.title) end end end diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index de4f94fff2f..c0316b073ad 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -4,17 +4,23 @@ describe "User Feed", feature: true do describe "GET /" do let!(:user) { create(:user) } - context "user atom feed via private token" do + context 'user atom feed via private token' do it "should render user atom feed" do visit user_path(user, :atom, private_token: user.private_token) - body.should have_selector("feed title") + expect(body).to have_selector('feed title') end end context 'feed content' do let(:project) { create(:project) } - let(:issue) { create(:issue, project: project, author: user, description: '') } - let(:note) { create(:note, noteable: issue, author: user, note: 'Bug confirmed', project: project) } + let(:issue) do + create(:issue, project: project, + author: user, description: '') + end + let(:note) do + create(:note, noteable: issue, author: user, + note: 'Bug confirmed', project: project) + end before do project.team << [user, :master] @@ -23,11 +29,11 @@ describe "User Feed", feature: true do visit user_path(user, :atom, private_token: user.private_token) end - it "should have issue opened event" do + it 'should have issue opened event' do expect(body).to have_content("#{safe_name} opened issue ##{issue.iid}") end - it "should have issue comment event" do + it 'should have issue comment event' do expect(body). to have_content("#{safe_name} commented on issue ##{issue.iid}") end diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb index 9f50d1c9738..fca1a06eb88 100644 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ b/spec/features/gitlab_flavored_markdown_spec.rb @@ -23,27 +23,27 @@ describe "GitLab Flavored Markdown", feature: true do describe "for commits" do it "should render title in commits#index" do - visit project_commits_path(project, 'master', limit: 1) + visit namespace_project_commits_path(project.namespace, project, 'master', limit: 1) - page.should have_link("##{issue.iid}") + expect(page).to have_link("##{issue.iid}") end it "should render title in commits#show" do - visit project_commit_path(project, commit) + visit namespace_project_commit_path(project.namespace, project, commit) - page.should have_link("##{issue.iid}") + expect(page).to have_link("##{issue.iid}") end it "should render description in commits#show" do - visit project_commit_path(project, commit) + visit namespace_project_commit_path(project.namespace, project, commit) - page.should have_link("@#{fred.username}") + expect(page).to have_link("@#{fred.username}") end it "should render title in repositories#branches" do - visit project_branches_path(project) + visit namespace_project_branches_path(project.namespace, project) - page.should have_link("##{issue.iid}") + expect(page).to have_link("##{issue.iid}") end end @@ -62,21 +62,21 @@ describe "GitLab Flavored Markdown", feature: true do end it "should render subject in issues#index" do - visit project_issues_path(project) + visit namespace_project_issues_path(project.namespace, project) - page.should have_link("##{@other_issue.iid}") + expect(page).to have_link("##{@other_issue.iid}") end it "should render subject in issues#show" do - visit project_issue_path(project, @issue) + visit namespace_project_issue_path(project.namespace, project, @issue) - page.should have_link("##{@other_issue.iid}") + expect(page).to have_link("##{@other_issue.iid}") end it "should render details in issues#show" do - visit project_issue_path(project, @issue) + visit namespace_project_issue_path(project.namespace, project, @issue) - page.should have_link("@#{fred.username}") + expect(page).to have_link("@#{fred.username}") end end @@ -87,15 +87,15 @@ describe "GitLab Flavored Markdown", feature: true do end it "should render title in merge_requests#index" do - visit project_merge_requests_path(project) + visit namespace_project_merge_requests_path(project.namespace, project) - page.should have_link("##{issue.iid}") + expect(page).to have_link("##{issue.iid}") end it "should render title in merge_requests#show" do - visit project_merge_request_path(project, @merge_request) + visit namespace_project_merge_request_path(project.namespace, project, @merge_request) - page.should have_link("##{issue.iid}") + expect(page).to have_link("##{issue.iid}") end end @@ -109,21 +109,21 @@ describe "GitLab Flavored Markdown", feature: true do end it "should render title in milestones#index" do - visit project_milestones_path(project) + visit namespace_project_milestones_path(project.namespace, project) - page.should have_link("##{issue.iid}") + expect(page).to have_link("##{issue.iid}") end it "should render title in milestones#show" do - visit project_milestone_path(project, @milestone) + visit namespace_project_milestone_path(project.namespace, project, @milestone) - page.should have_link("##{issue.iid}") + expect(page).to have_link("##{issue.iid}") end it "should render description in milestones#show" do - visit project_milestone_path(project, @milestone) + visit namespace_project_milestone_path(project.namespace, project, @milestone) - page.should have_link("@#{fred.username}") + expect(page).to have_link("@#{fred.username}") end end end diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb index 89129cfc7cd..41088ce8271 100644 --- a/spec/features/help_pages_spec.rb +++ b/spec/features/help_pages_spec.rb @@ -7,7 +7,7 @@ describe 'Help Pages', feature: true do end it 'replace the variable $your_email with the email of the user' do visit help_page_path(category: 'ssh', file: 'README.md') - page.should have_content("ssh-keygen -t rsa -C \"#{@user.email}\"") + expect(page).to have_content("ssh-keygen -t rsa -C \"#{@user.email}\"") end end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 29aeb6a400a..a2db57ad908 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Issues", feature: true do +describe 'Issues', feature: true do include SortingHelper let(:project) { create(:project) } @@ -12,7 +12,7 @@ describe "Issues", feature: true do project.team << [[@user, user2], :developer] end - describe "Edit issue" do + describe 'Edit issue' do let!(:issue) do create(:issue, author: @user, @@ -21,34 +21,38 @@ describe "Issues", feature: true do end before do - visit project_issues_path(project) + visit namespace_project_issues_path(project.namespace, project) click_link "Edit" end - it "should open new issue popup" do - page.should have_content("Issue ##{issue.iid}") + it 'should open new issue popup' do + expect(page).to have_content("Issue ##{issue.iid}") end - describe "fill in" do + describe 'fill in' do before do - fill_in "issue_title", with: "bug 345" - fill_in "issue_description", with: "bug description" + fill_in 'issue_title', with: 'bug 345' + fill_in 'issue_description', with: 'bug description' end - it { expect { click_button "Save changes" }.to_not change {Issue.count} } + it 'does not change issue count' do + expect { + click_button 'Save changes' + }.to_not change { Issue.count } + end - it "should update issue fields" do - click_button "Save changes" + it 'should update issue fields' do + click_button 'Save changes' - page.should have_content @user.name - page.should have_content "bug 345" - page.should have_content project.name + expect(page).to have_content @user.name + expect(page).to have_content 'bug 345' + expect(page).to have_content project.name end end end - describe "Editing issue assignee" do + describe 'Editing issue assignee' do let!(:issue) do create(:issue, author: @user, @@ -56,23 +60,23 @@ describe "Issues", feature: true do project: project) end - it 'allows user to select unasigned', :js => true do - visit edit_project_issue_path(project, issue) + it 'allows user to select unasigned', js: true do + visit edit_namespace_project_issue_path(project.namespace, project, issue) - page.should have_content "Assign to #{@user.name}" + expect(page).to have_content "Assign to #{@user.name}" first('#s2id_issue_assignee_id').click sleep 2 # wait for ajax stuff to complete first('.user-result').click - click_button "Save changes" + click_button 'Save changes' - page.should have_content 'Assignee: none' - issue.reload.assignee.should be_nil + expect(page).to have_content 'Assignee: none' + expect(issue.reload.assignee).to be_nil end end - describe "Filter issue" do + describe 'Filter issue' do before do ['foobar', 'barbaz', 'gitlab'].each do |title| create(:issue, @@ -90,75 +94,79 @@ describe "Issues", feature: true do let(:issue) { @issue } - it "should allow filtering by issues with no specified milestone" do - visit project_issues_path(project, milestone_id: '0') + it 'should allow filtering by issues with no specified milestone' do + visit namespace_project_issues_path(project.namespace, project, milestone_id: '0') - page.should_not have_content 'foobar' - page.should have_content 'barbaz' - page.should have_content 'gitlab' + expect(page).not_to have_content 'foobar' + expect(page).to have_content 'barbaz' + expect(page).to have_content 'gitlab' end - it "should allow filtering by a specified milestone" do - visit project_issues_path(project, milestone_id: issue.milestone.id) + it 'should allow filtering by a specified milestone' do + visit namespace_project_issues_path(project.namespace, project, milestone_id: issue.milestone.id) - page.should have_content 'foobar' - page.should_not have_content 'barbaz' - page.should_not have_content 'gitlab' + expect(page).to have_content 'foobar' + expect(page).not_to have_content 'barbaz' + expect(page).not_to have_content 'gitlab' end - it "should allow filtering by issues with no specified assignee" do - visit project_issues_path(project, assignee_id: '0') + it 'should allow filtering by issues with no specified assignee' do + visit namespace_project_issues_path(project.namespace, project, assignee_id: '0') - page.should have_content 'foobar' - page.should_not have_content 'barbaz' - page.should_not have_content 'gitlab' + expect(page).to have_content 'foobar' + expect(page).not_to have_content 'barbaz' + expect(page).not_to have_content 'gitlab' end - it "should allow filtering by a specified assignee" do - visit project_issues_path(project, assignee_id: @user.id) + it 'should allow filtering by a specified assignee' do + visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id) - page.should_not have_content 'foobar' - page.should have_content 'barbaz' - page.should have_content 'gitlab' + expect(page).not_to have_content 'foobar' + expect(page).to have_content 'barbaz' + expect(page).to have_content 'gitlab' end end describe 'filter issue' do titles = ['foo','bar','baz'] titles.each_with_index do |title, index| - let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) } + let!(title.to_sym) do + create(:issue, title: title, + project: project, + created_at: Time.now - (index * 60)) + end end let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') } let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') } it 'sorts by newest' do - visit project_issues_path(project, sort: sort_value_recently_created) + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_recently_created) - first_issue.should include("foo") - last_issue.should include("baz") + expect(first_issue).to include('foo') + expect(last_issue).to include('baz') end it 'sorts by oldest' do - visit project_issues_path(project, sort: sort_value_oldest_created) + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_oldest_created) - first_issue.should include("baz") - last_issue.should include("foo") + expect(first_issue).to include('baz') + expect(last_issue).to include('foo') end it 'sorts by most recently updated' do baz.updated_at = Time.now + 100 baz.save - visit project_issues_path(project, sort: sort_value_recently_updated) + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_recently_updated) - first_issue.should include("baz") + expect(first_issue).to include('baz') end it 'sorts by least recently updated' do baz.updated_at = Time.now - 100 baz.save - visit project_issues_path(project, sort: sort_value_oldest_updated) + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_oldest_updated) - first_issue.should include("baz") + expect(first_issue).to include('baz') end describe 'sorting by milestone' do @@ -170,15 +178,15 @@ describe "Issues", feature: true do end it 'sorts by recently due milestone' do - visit project_issues_path(project, sort: sort_value_milestone_soon) + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_soon) - first_issue.should include("foo") + expect(first_issue).to include('foo') end it 'sorts by least recently due milestone' do - visit project_issues_path(project, sort: sort_value_milestone_later) + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_later) - first_issue.should include("bar") + expect(first_issue).to include('bar') end end @@ -193,11 +201,13 @@ describe "Issues", feature: true do end it 'sorts with a filter applied' do - visit project_issues_path(project, sort: sort_value_oldest_created, assignee_id: user2.id) + visit namespace_project_issues_path(project.namespace, project, + sort: sort_value_oldest_created, + assignee_id: user2.id) - first_issue.should include("bar") - last_issue.should include("foo") - page.should_not have_content 'baz' + expect(first_issue).to include('bar') + expect(last_issue).to include('foo') + expect(page).not_to have_content 'baz' end end end @@ -208,13 +218,15 @@ describe "Issues", feature: true do context 'by autorized user' do it 'with dropdown menu' do - visit project_issue_path(project, issue) + visit namespace_project_issue_path(project.namespace, project, issue) - find('.edit-issue.inline-update #issue_assignee_id').set project.team.members.first.id + find('.edit-issue.inline-update #issue_assignee_id'). + set project.team.members.first.id click_button 'Update Issue' - page.should have_content "Assignee:" - has_select?('issue_assignee_id', :selected => project.team.members.first.name) + expect(page).to have_content 'Assignee:' + has_select?('issue_assignee_id', + selected: project.team.members.first.name) end end @@ -228,12 +240,12 @@ describe "Issues", feature: true do issue.save end - it "shows assignee text", js: true do + it 'shows assignee text', js: true do logout login_with guest - visit project_issue_path(project, issue) - page.should have_content issue.assignee.name + visit namespace_project_issue_path(project.namespace, project, issue) + expect(page).to have_content issue.assignee.name end end end @@ -245,14 +257,15 @@ describe "Issues", feature: true do context 'by authorized user' do it 'with dropdown menu' do - visit project_issue_path(project, issue) + visit namespace_project_issue_path(project.namespace, project, issue) - find('.edit-issue.inline-update').select(milestone.title, from: 'issue_milestone_id') + find('.edit-issue.inline-update'). + select(milestone.title, from: 'issue_milestone_id') click_button 'Update Issue' - page.should have_content "Milestone changed to #{milestone.title}" - page.should have_content "Milestone: #{milestone.title}" - has_select?('issue_assignee_id', :selected => milestone.title) + expect(page).to have_content "Milestone changed to #{milestone.title}" + expect(page).to have_content "Milestone: #{milestone.title}" + has_select?('issue_assignee_id', selected: milestone.title) end end @@ -265,12 +278,12 @@ describe "Issues", feature: true do issue.save end - it "shows milestone text", js: true do + it 'shows milestone text', js: true do logout login_with guest - visit project_issue_path(project, issue) - page.should have_content milestone.title + visit namespace_project_issue_path(project.namespace, project, issue) + expect(page).to have_content milestone.title end end @@ -283,25 +296,25 @@ describe "Issues", feature: true do end it 'allows user to remove assignee', :js => true do - visit project_issue_path(project, issue) - page.should have_content "Assignee: #{user2.name}" + visit namespace_project_issue_path(project.namespace, project, issue) + expect(page).to have_content "Assignee: #{user2.name}" first('#s2id_issue_assignee_id').click sleep 2 # wait for ajax stuff to complete first('.user-result').click - page.should have_content 'Assignee: none' + expect(page).to have_content 'Assignee: none' sleep 2 # wait for ajax stuff to complete - issue.reload.assignee.should be_nil + expect(issue.reload.assignee).to be_nil end end end def first_issue - all("ul.issues-list li").first.text + all('ul.issues-list li').first.text end def last_issue - all("ul.issues-list li").last.text + all('ul.issues-list li').last.text end end diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index f66f5e7cb19..c47368b1fda 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -3,31 +3,34 @@ require 'spec_helper' describe 'Comments' do include RepoHelpers - describe "On a merge request", js: true, feature: true do + describe 'On a merge request', js: true, feature: true do let!(:merge_request) { create(:merge_request) } let!(:project) { merge_request.source_project } - let!(:note) { create(:note_on_merge_request, :with_attachment, project: project) } + let!(:note) do + create(:note_on_merge_request, :with_attachment, project: project) + end before do login_as :admin - visit project_merge_request_path(project, merge_request) + visit namespace_project_merge_request_path(project.namespace, project, merge_request) end subject { page } - describe "the note form" do + describe 'the note form' do it 'should be valid' do - should have_css(".js-main-target-form", visible: true, count: 1) - find(".js-main-target-form input[type=submit]").value.should == "Add Comment" + is_expected.to have_css('.js-main-target-form', visible: true, count: 1) + expect(find('.js-main-target-form input[type=submit]').value). + to eq('Add Comment') within('.js-main-target-form') do expect(page).not_to have_link('Cancel') end end - describe "with text" do + describe 'with text' do before do - within(".js-main-target-form") do - fill_in "note[note]", with: "This is awesome" + within('.js-main-target-form') do + fill_in 'note[note]', with: 'This is awesome' end end @@ -40,41 +43,45 @@ describe 'Comments' do end end - describe "when posting a note" do + describe 'when posting a note' do before do - within(".js-main-target-form") do - fill_in "note[note]", with: "This is awsome!" + within('.js-main-target-form') do + fill_in 'note[note]', with: 'This is awsome!' find('.js-md-preview-button').click - click_button "Add Comment" + click_button 'Add Comment' end end it 'should be added and form reset' do - should have_content("This is awsome!") + is_expected.to have_content('This is awsome!') within('.js-main-target-form') do expect(page).to have_no_field('note[note]', with: 'This is awesome!') expect(page).to have_css('.js-md-preview', visible: :hidden) end - within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } + within('.js-main-target-form') do + is_expected.to have_css('.js-note-text', visible: true) + end end end - describe "when editing a note", js: true do - it "should contain the hidden edit form" do - within("#note_#{note.id}") { should have_css(".note-edit-form", visible: false) } + describe 'when editing a note', js: true do + it 'should contain the hidden edit form' do + within("#note_#{note.id}") do + is_expected.to have_css('.note-edit-form', visible: false) + end end - describe "editing the note" do + describe 'editing the note' do before do find('.note').hover find(".js-note-edit").click end - it "should show the note edit form and hide the note body" do + it 'should show the note edit form and hide the note body' do within("#note_#{note.id}") do - find(".current-note-edit-form", visible: true).should be_visible - find(".note-edit-form", visible: true).should be_visible - find(:css, ".note-text", visible: false).should_not be_visible + expect(find('.current-note-edit-form', visible: true)).to be_visible + expect(find('.note-edit-form', visible: true)).to be_visible + expect(find(:css, '.note-body > .note-text', visible: false)).not_to be_visible end end @@ -87,114 +94,122 @@ describe 'Comments' do #end #end - it "appends the edited at time to the note" do - within(".current-note-edit-form") do - fill_in "note[note]", with: "Some new content" - find(".btn-save").click + it 'appends the edited at time to the note' do + within('.current-note-edit-form') do + fill_in 'note[note]', with: 'Some new content' + find('.btn-save').click end within("#note_#{note.id}") do - should have_css(".note_edited_ago") - find(".note_edited_ago").text.should match(/less than a minute ago/) + is_expected.to have_css('.note_edited_ago') + expect(find('.note_edited_ago').text). + to match(/less than a minute ago/) end end end - describe "deleting an attachment" do + describe 'deleting an attachment' do before do find('.note').hover - find(".js-note-edit").click + find('.js-note-edit').click end - it "shows the delete link" do - within(".note-attachment") do - should have_css(".js-note-attachment-delete") + it 'shows the delete link' do + within('.note-attachment') do + is_expected.to have_css('.js-note-attachment-delete') end end - it "removes the attachment div and resets the edit form" do - find(".js-note-attachment-delete").click - should_not have_css(".note-attachment") - find(".current-note-edit-form", visible: false).should_not be_visible + it 'removes the attachment div and resets the edit form' do + find('.js-note-attachment-delete').click + is_expected.not_to have_css('.note-attachment') + expect(find('.current-note-edit-form', visible: false)). + not_to be_visible end end end end - describe "On a merge request diff", js: true, feature: true do + describe 'On a merge request diff', js: true, feature: true do let(:merge_request) { create(:merge_request) } let(:project) { merge_request.source_project } before do login_as :admin - visit diffs_project_merge_request_path(project, merge_request) + visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request) end subject { page } - describe "when adding a note" do + describe 'when adding a note' do before do click_diff_line end - describe "the notes holder" do - it { should have_css(".js-temp-notes-holder") } + describe 'the notes holder' do + it { is_expected.to have_css('.js-temp-notes-holder') } - it { within(".js-temp-notes-holder") { should have_css(".new_note") } } + it 'has .new_note css class' do + within('.js-temp-notes-holder') do + expect(subject).to have_css('.new_note') + end + end end - describe "the note form" do + describe 'the note form' do it "shouldn't add a second form for same row" do click_diff_line - should have_css("tr[id='#{line_code}'] + .js-temp-notes-holder form", count: 1) + is_expected. + to have_css("tr[id='#{line_code}'] + .js-temp-notes-holder form", + count: 1) end - it "should be removed when canceled" do + it 'should be removed when canceled' do within(".diff-file form[rel$='#{line_code}']") do - find(".js-close-discussion-note-form").trigger("click") + find('.js-close-discussion-note-form').trigger('click') end - should have_no_css(".js-temp-notes-holder") + is_expected.to have_no_css('.js-temp-notes-holder') end end end - describe "with muliple note forms" do + describe 'with muliple note forms' do before do click_diff_line click_diff_line(line_code_2) end - it { should have_css(".js-temp-notes-holder", count: 2) } + it { is_expected.to have_css('.js-temp-notes-holder', count: 2) } - describe "previewing them separately" do + describe 'previewing them separately' do before do # add two separate texts and trigger previews on both within("tr[id='#{line_code}'] + .js-temp-notes-holder") do - fill_in "note[note]", with: "One comment on line 7" + fill_in 'note[note]', with: 'One comment on line 7' find('.js-md-preview-button').click end within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do - fill_in "note[note]", with: "Another comment on line 10" + fill_in 'note[note]', with: 'Another comment on line 10' find('.js-md-preview-button').click end end end - describe "posting a note" do + describe 'posting a note' do before do within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do - fill_in "note[note]", with: "Another comment on line 10" - click_button("Add Comment") + fill_in 'note[note]', with: 'Another comment on line 10' + click_button('Add Comment') end end it 'should be added as discussion' do - should have_content("Another comment on line 10") - should have_css(".notes_holder") - should have_css(".notes_holder .note", count: 1) - should have_button('Reply') + is_expected.to have_content('Another comment on line 10') + is_expected.to have_css('.notes_holder') + is_expected.to have_css('.notes_holder .note', count: 1) + is_expected.to have_button('Reply') end end end diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 4a76e89fd34..3d36a3c02d0 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -1,35 +1,35 @@ require 'spec_helper' -describe "Profile account page", feature: true do +describe 'Profile account page', feature: true do let(:user) { create(:user) } before do login_as :user end - describe "when signup is enabled" do + describe 'when signup is enabled' do before do ApplicationSetting.any_instance.stub(signup_enabled?: true) visit profile_account_path end - it { page.should have_content("Remove account") } + it { expect(page).to have_content('Remove account') } - it "should delete the account" do - expect { click_link "Delete account" }.to change {User.count}.by(-1) - current_path.should == new_user_session_path + it 'should delete the account' do + expect { click_link 'Delete account' }.to change { User.count }.by(-1) + expect(current_path).to eq(new_user_session_path) end end - describe "when signup is disabled" do + describe 'when signup is disabled' do before do ApplicationSetting.any_instance.stub(signup_enabled?: false) visit profile_account_path end - it "should not have option to remove account" do - page.should_not have_content("Remove account") - current_path.should == profile_account_path + it 'should not have option to remove account' do + expect(page).not_to have_content('Remove account') + expect(current_path).to eq(profile_account_path) end end end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index d291621935b..cae11be7cdd 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -7,7 +7,7 @@ describe "Projects", feature: true, js: true do before do @project = create(:project, namespace: @user.namespace) @project.team << [@user, :master] - visit edit_project_path(@project) + visit edit_namespace_project_path(@project.namespace, @project) end it "should remove project" do diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index cce9f06cb69..73987739a7a 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -14,7 +14,7 @@ describe "Search", feature: true do end it "should show project in search results" do - page.should have_content @project.name + expect(page).to have_content @project.name end end diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb index 1cca82cef64..67238e3ab76 100644 --- a/spec/features/security/dashboard_access_spec.rb +++ b/spec/features/security/dashboard_access_spec.rb @@ -4,52 +4,60 @@ describe "Dashboard access", feature: true do describe "GET /dashboard" do subject { dashboard_path } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /dashboard/issues" do subject { issues_dashboard_path } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /dashboard/merge_requests" do subject { merge_requests_dashboard_path } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end - describe "GET /dashboard/projects" do - subject { projects_dashboard_path } + describe "GET /dashboard/projects/starred" do + subject { starred_dashboard_projects_path } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /help" do subject { help_path } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /projects/new" do - it { new_project_path.should be_allowed_for :admin } - it { new_project_path.should be_allowed_for :user } - it { new_project_path.should be_denied_for :visitor } + it { expect(new_project_path).to be_allowed_for :admin } + it { expect(new_project_path).to be_allowed_for :user } + it { expect(new_project_path).to be_denied_for :visitor } end describe "GET /groups/new" do - it { new_group_path.should be_allowed_for :admin } - it { new_group_path.should be_allowed_for :user } - it { new_group_path.should be_denied_for :visitor } + it { expect(new_group_path).to be_allowed_for :admin } + it { expect(new_group_path).to be_allowed_for :user } + it { expect(new_group_path).to be_denied_for :visitor } + end + + describe "GET /profile/groups" do + subject { dashboard_groups_path } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/features/security/group/group_access_spec.rb b/spec/features/security/group/group_access_spec.rb index 44de499e6d2..e0c5cbf4d3d 100644 --- a/spec/features/security/group/group_access_spec.rb +++ b/spec/features/security/group/group_access_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' describe "Group access", feature: true do describe "GET /projects/new" do - it { new_group_path.should be_allowed_for :admin } - it { new_group_path.should be_allowed_for :user } - it { new_group_path.should be_denied_for :visitor } + it { expect(new_group_path).to be_allowed_for :admin } + it { expect(new_group_path).to be_allowed_for :user } + it { expect(new_group_path).to be_denied_for :visitor } end describe "Group" do @@ -26,73 +26,73 @@ describe "Group access", feature: true do describe "GET /groups/:path" do subject { group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/issues" do subject { issues_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/merge_requests" do subject { merge_requests_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/members" do subject { members_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/edit" do subject { edit_group_path(group) } - it { should be_allowed_for owner } - it { should be_denied_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/projects" do subject { projects_group_path(group) } - it { should be_allowed_for owner } - it { should be_denied_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end end diff --git a/spec/features/security/group/internal_group_access_spec.rb b/spec/features/security/group/internal_group_access_spec.rb index da5c6eb4e91..5279a1bc13a 100644 --- a/spec/features/security/group/internal_group_access_spec.rb +++ b/spec/features/security/group/internal_group_access_spec.rb @@ -22,61 +22,61 @@ describe "Group with internal project access", feature: true do describe "GET /groups/:path" do subject { group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/issues" do subject { issues_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/merge_requests" do subject { merge_requests_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/members" do subject { members_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /groups/:path/edit" do subject { edit_group_path(group) } - it { should be_allowed_for owner } - it { should be_denied_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end end diff --git a/spec/features/security/group/mixed_group_access_spec.rb b/spec/features/security/group/mixed_group_access_spec.rb index c9889d99590..efd14858b98 100644 --- a/spec/features/security/group/mixed_group_access_spec.rb +++ b/spec/features/security/group/mixed_group_access_spec.rb @@ -23,61 +23,61 @@ describe "Group access", feature: true do describe "GET /groups/:path" do subject { group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/issues" do subject { issues_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/merge_requests" do subject { merge_requests_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/members" do subject { members_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/edit" do subject { edit_group_path(group) } - it { should be_allowed_for owner } - it { should be_denied_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end end diff --git a/spec/features/security/group/public_group_access_spec.rb b/spec/features/security/group/public_group_access_spec.rb index 2e76ab154ff..c7e3d0a8a40 100644 --- a/spec/features/security/group/public_group_access_spec.rb +++ b/spec/features/security/group/public_group_access_spec.rb @@ -22,61 +22,61 @@ describe "Group with public project access", feature: true do describe "GET /groups/:path" do subject { group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/issues" do subject { issues_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/merge_requests" do subject { merge_requests_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/members" do subject { members_group_path(group) } - it { should be_allowed_for owner } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /groups/:path/edit" do subject { edit_group_path(group) } - it { should be_allowed_for owner } - it { should be_denied_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_denied_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end end diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb index 4efc0ffdcd3..2512a9c0e3d 100644 --- a/spec/features/security/profile_access_spec.rb +++ b/spec/features/security/profile_access_spec.rb @@ -1,76 +1,65 @@ require 'spec_helper' -describe "Users Security", feature: true do - describe "Project" do - before do - @u1 = create(:user) - end - - describe "GET /login" do - it { new_user_session_path.should_not be_404_for :visitor } - end - - describe "GET /profile/keys" do - subject { profile_keys_path } +describe "Profile access", feature: true do + before do + @u1 = create(:user) + end - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end + describe "GET /login" do + it { expect(new_user_session_path).not_to be_404_for :visitor } + end - describe "GET /profile" do - subject { profile_path } + describe "GET /profile/keys" do + subject { profile_keys_path } - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end + it { is_expected.to be_allowed_for @u1 } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end - describe "GET /profile/account" do - subject { profile_account_path } + describe "GET /profile" do + subject { profile_path } - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end + it { is_expected.to be_allowed_for @u1 } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end - describe "GET /profile/design" do - subject { design_profile_path } + describe "GET /profile/account" do + subject { profile_account_path } - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end + it { is_expected.to be_allowed_for @u1 } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end - describe "GET /profile/history" do - subject { history_profile_path } + describe "GET /profile/design" do + subject { design_profile_path } - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end + it { is_expected.to be_allowed_for @u1 } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end - describe "GET /profile/notifications" do - subject { profile_notifications_path } + describe "GET /profile/history" do + subject { history_profile_path } - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end + it { is_expected.to be_allowed_for @u1 } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end - describe "GET /profile/groups" do - subject { profile_groups_path } + describe "GET /profile/notifications" do + subject { profile_notifications_path } - it { should be_allowed_for @u1 } - it { should be_allowed_for :admin } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } - end + it { is_expected.to be_allowed_for @u1 } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 598d554a946..322697bced8 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -18,207 +18,210 @@ describe "Internal Project Access", feature: true do describe "Project should be internal" do subject { project } - its(:internal?) { should be_true } + describe '#internal?' do + subject { super().internal? } + it { is_expected.to be_truthy } + end end describe "GET /:project_path" do - subject { project_path(project) } + subject { namespace_project_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/tree/master" do - subject { project_tree_path(project, project.repository.root_ref) } + subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commits/master" do - subject { project_commits_path(project, project.repository.root_ref, limit: 1) } + subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commit/:sha" do - subject { project_commit_path(project, project.repository.commit) } + subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/compare" do - subject { project_compare_index_path(project) } + subject { namespace_project_compare_index_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/team" do - subject { project_team_index_path(project) } + subject { namespace_project_team_index_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/blob" do before do commit = project.repository.commit path = '.gitignore' - @blob_path = project_blob_path(project, File.join(commit.id, path)) + @blob_path = namespace_project_blob_path(project.namespace, project, File.join(commit.id, path)) end - it { @blob_path.should be_allowed_for master } - it { @blob_path.should be_allowed_for reporter } - it { @blob_path.should be_allowed_for :admin } - it { @blob_path.should be_allowed_for guest } - it { @blob_path.should be_allowed_for :user } - it { @blob_path.should be_denied_for :visitor } + it { expect(@blob_path).to be_allowed_for master } + it { expect(@blob_path).to be_allowed_for reporter } + it { expect(@blob_path).to be_allowed_for :admin } + it { expect(@blob_path).to be_allowed_for guest } + it { expect(@blob_path).to be_allowed_for :user } + it { expect(@blob_path).to be_denied_for :visitor } end describe "GET /:project_path/edit" do - subject { edit_project_path(project) } + subject { edit_namespace_project_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/deploy_keys" do - subject { project_deploy_keys_path(project) } + subject { namespace_project_deploy_keys_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/issues" do - subject { project_issues_path(project) } + subject { namespace_project_issues_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/snippets" do - subject { project_snippets_path(project) } + subject { namespace_project_snippets_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/snippets/new" do - subject { new_project_snippet_path(project) } + subject { new_namespace_project_snippet_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/merge_requests" do - subject { project_merge_requests_path(project) } + subject { namespace_project_merge_requests_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/merge_requests/new" do - subject { new_project_merge_request_path(project) } + subject { new_namespace_project_merge_request_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/branches" do - subject { project_branches_path(project) } + subject { namespace_project_branches_path(project.namespace, project) } before do # Speed increase - Project.any_instance.stub(:branches).and_return([]) + allow_any_instance_of(Project).to receive(:branches).and_return([]) end - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/tags" do - subject { project_tags_path(project) } + subject { namespace_project_tags_path(project.namespace, project) } before do # Speed increase - Project.any_instance.stub(:tags).and_return([]) + allow_any_instance_of(Project).to receive(:tags).and_return([]) end - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/hooks" do - subject { project_hooks_path(project) } - - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + subject { namespace_project_hooks_path(project.namespace, project) } + + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index b1d4c79e05b..ea146c3f0e4 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -18,185 +18,188 @@ describe "Private Project Access", feature: true do describe "Project should be private" do subject { project } - its(:private?) { should be_true } + describe '#private?' do + subject { super().private? } + it { is_expected.to be_truthy } + end end describe "GET /:project_path" do - subject { project_path(project) } + subject { namespace_project_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/tree/master" do - subject { project_tree_path(project, project.repository.root_ref) } + subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commits/master" do - subject { project_commits_path(project, project.repository.root_ref, limit: 1) } + subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/commit/:sha" do - subject { project_commit_path(project, project.repository.commit) } + subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/compare" do - subject { project_compare_index_path(project) } + subject { namespace_project_compare_index_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/team" do - subject { project_team_index_path(project) } + subject { namespace_project_team_index_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/blob" do before do commit = project.repository.commit path = '.gitignore' - @blob_path = project_blob_path(project, File.join(commit.id, path)) + @blob_path = namespace_project_blob_path(project.namespace, project, File.join(commit.id, path)) end - it { @blob_path.should be_allowed_for master } - it { @blob_path.should be_allowed_for reporter } - it { @blob_path.should be_allowed_for :admin } - it { @blob_path.should be_denied_for guest } - it { @blob_path.should be_denied_for :user } - it { @blob_path.should be_denied_for :visitor } + it { expect(@blob_path).to be_allowed_for master } + it { expect(@blob_path).to be_allowed_for reporter } + it { expect(@blob_path).to be_allowed_for :admin } + it { expect(@blob_path).to be_denied_for guest } + it { expect(@blob_path).to be_denied_for :user } + it { expect(@blob_path).to be_denied_for :visitor } end describe "GET /:project_path/edit" do - subject { edit_project_path(project) } + subject { edit_namespace_project_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/deploy_keys" do - subject { project_deploy_keys_path(project) } + subject { namespace_project_deploy_keys_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/issues" do - subject { project_issues_path(project) } + subject { namespace_project_issues_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/snippets" do - subject { project_snippets_path(project) } + subject { namespace_project_snippets_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/merge_requests" do - subject { project_merge_requests_path(project) } + subject { namespace_project_merge_requests_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/branches" do - subject { project_branches_path(project) } + subject { namespace_project_branches_path(project.namespace, project) } before do # Speed increase - Project.any_instance.stub(:branches).and_return([]) + allow_any_instance_of(Project).to receive(:branches).and_return([]) end - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/tags" do - subject { project_tags_path(project) } + subject { namespace_project_tags_path(project.namespace, project) } before do # Speed increase - Project.any_instance.stub(:tags).and_return([]) + allow_any_instance_of(Project).to receive(:tags).and_return([]) end - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/hooks" do - subject { project_hooks_path(project) } - - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + subject { namespace_project_hooks_path(project.namespace, project) } + + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index a4c8a2be25a..8ee9199ff29 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -23,207 +23,210 @@ describe "Public Project Access", feature: true do describe "Project should be public" do subject { project } - its(:public?) { should be_true } + describe '#public?' do + subject { super().public? } + it { is_expected.to be_truthy } + end end describe "GET /:project_path" do - subject { project_path(project) } + subject { namespace_project_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/tree/master" do - subject { project_tree_path(project, project.repository.root_ref) } + subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/commits/master" do - subject { project_commits_path(project, project.repository.root_ref, limit: 1) } + subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/commit/:sha" do - subject { project_commit_path(project, project.repository.commit) } + subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/compare" do - subject { project_compare_index_path(project) } + subject { namespace_project_compare_index_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/team" do - subject { project_team_index_path(project) } + subject { namespace_project_team_index_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/blob" do before do commit = project.repository.commit path = '.gitignore' - @blob_path = project_blob_path(project, File.join(commit.id, path)) + @blob_path = namespace_project_blob_path(project.namespace, project, File.join(commit.id, path)) end - it { @blob_path.should be_allowed_for master } - it { @blob_path.should be_allowed_for reporter } - it { @blob_path.should be_allowed_for :admin } - it { @blob_path.should be_allowed_for guest } - it { @blob_path.should be_allowed_for :user } - it { @blob_path.should be_allowed_for :visitor } + it { expect(@blob_path).to be_allowed_for master } + it { expect(@blob_path).to be_allowed_for reporter } + it { expect(@blob_path).to be_allowed_for :admin } + it { expect(@blob_path).to be_allowed_for guest } + it { expect(@blob_path).to be_allowed_for :user } + it { expect(@blob_path).to be_allowed_for :visitor } end describe "GET /:project_path/edit" do - subject { edit_project_path(project) } + subject { edit_namespace_project_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/deploy_keys" do - subject { project_deploy_keys_path(project) } + subject { namespace_project_deploy_keys_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/issues" do - subject { project_issues_path(project) } + subject { namespace_project_issues_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/snippets" do - subject { project_snippets_path(project) } + subject { namespace_project_snippets_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/snippets/new" do - subject { new_project_snippet_path(project) } + subject { new_namespace_project_snippet_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/merge_requests" do - subject { project_merge_requests_path(project) } + subject { namespace_project_merge_requests_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/merge_requests/new" do - subject { new_project_merge_request_path(project) } + subject { new_namespace_project_merge_request_path(project.namespace, project) } - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end describe "GET /:project_path/branches" do - subject { project_branches_path(project) } + subject { namespace_project_branches_path(project.namespace, project) } before do # Speed increase - Project.any_instance.stub(:branches).and_return([]) + allow_any_instance_of(Project).to receive(:branches).and_return([]) end - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/tags" do - subject { project_tags_path(project) } + subject { namespace_project_tags_path(project.namespace, project) } before do # Speed increase - Project.any_instance.stub(:tags).and_return([]) + allow_any_instance_of(Project).to receive(:tags).and_return([]) end - it { should be_allowed_for master } - it { should be_allowed_for reporter } - it { should be_allowed_for :admin } - it { should be_allowed_for guest } - it { should be_allowed_for :user } - it { should be_allowed_for :visitor } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } end describe "GET /:project_path/hooks" do - subject { project_hooks_path(project) } - - it { should be_allowed_for master } - it { should be_denied_for reporter } - it { should be_allowed_for :admin } - it { should be_denied_for guest } - it { should be_denied_for :user } - it { should be_denied_for :visitor } + subject { namespace_project_hooks_path(project.namespace, project) } + + it { is_expected.to be_allowed_for master } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } end end diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index 06e247aea61..479fa950387 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -27,40 +27,40 @@ describe IssuesFinder do it 'should filter by all' do params = { scope: "all", state: 'opened' } issues = IssuesFinder.new.execute(user, params) - issues.size.should == 3 + expect(issues.size).to eq(3) end it 'should filter by assignee id' do params = { scope: "all", assignee_id: user.id, state: 'opened' } issues = IssuesFinder.new.execute(user, params) - issues.size.should == 2 + expect(issues.size).to eq(2) end it 'should filter by author id' do params = { scope: "all", author_id: user2.id, state: 'opened' } issues = IssuesFinder.new.execute(user, params) - issues.should == [issue3] + expect(issues).to eq([issue3]) end it 'should filter by milestone id' do params = { scope: "all", milestone_id: milestone.id, state: 'opened' } issues = IssuesFinder.new.execute(user, params) - issues.should == [issue1] + expect(issues).to eq([issue1]) end it 'should be empty for unauthorized user' do params = { scope: "all", state: 'opened' } issues = IssuesFinder.new.execute(nil, params) - issues.size.should be_zero + expect(issues.size).to be_zero end it 'should not include unauthorized issues' do params = { scope: "all", state: 'opened' } issues = IssuesFinder.new.execute(user2, params) - issues.size.should == 2 - issues.should_not include(issue1) - issues.should include(issue2) - issues.should include(issue3) + expect(issues.size).to eq(2) + expect(issues).not_to include(issue1) + expect(issues).to include(issue2) + expect(issues).to include(issue3) end end @@ -68,13 +68,13 @@ describe IssuesFinder do it 'should filter by assignee' do params = { scope: "assigned-to-me", state: 'opened' } issues = IssuesFinder.new.execute(user, params) - issues.size.should == 2 + expect(issues.size).to eq(2) end it 'should filter by project' do params = { scope: "assigned-to-me", state: 'opened', project_id: project1.id } issues = IssuesFinder.new.execute(user, params) - issues.size.should == 1 + expect(issues.size).to eq(1) end end end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 94b4d4c4ff4..8536377a7f0 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -21,13 +21,13 @@ describe MergeRequestsFinder do it 'should filter by scope' do params = { scope: 'authored', state: 'opened' } merge_requests = MergeRequestsFinder.new.execute(user, params) - merge_requests.size.should == 2 + expect(merge_requests.size).to eq(2) end it 'should filter by project' do params = { project_id: project1.id, scope: 'authored', state: 'opened' } merge_requests = MergeRequestsFinder.new.execute(user, params) - merge_requests.size.should == 1 + expect(merge_requests.size).to eq(1) end end end diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb index 4f8a5f909df..c83824b900d 100644 --- a/spec/finders/notes_finder_spec.rb +++ b/spec/finders/notes_finder_spec.rb @@ -21,7 +21,7 @@ describe NotesFinder do it 'should find all notes' do notes = NotesFinder.new.execute(project, user, params) - notes.size.should eq(2) + expect(notes.size).to eq(2) end it 'should raise an exception for an invalid target_type' do @@ -32,7 +32,7 @@ describe NotesFinder do it 'filters out old notes' do note2.update_attribute(:updated_at, 2.hours.ago) notes = NotesFinder.new.execute(project, user, params) - notes.should eq([note1]) + expect(notes).to eq([note1]) end end end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index 6e3ae4d615b..2ab71b05968 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -12,19 +12,19 @@ describe ProjectsFinder do context 'non authenticated' do subject { ProjectsFinder.new.execute(nil, group: group) } - it { should include(project1) } - it { should_not include(project2) } - it { should_not include(project3) } - it { should_not include(project4) } + it { is_expected.to include(project1) } + it { is_expected.not_to include(project2) } + it { is_expected.not_to include(project3) } + it { is_expected.not_to include(project4) } end context 'authenticated' do subject { ProjectsFinder.new.execute(user, group: group) } - it { should include(project1) } - it { should include(project2) } - it { should_not include(project3) } - it { should_not include(project4) } + it { is_expected.to include(project1) } + it { is_expected.to include(project2) } + it { is_expected.not_to include(project3) } + it { is_expected.not_to include(project4) } end context 'authenticated, project member' do @@ -32,10 +32,10 @@ describe ProjectsFinder do subject { ProjectsFinder.new.execute(user, group: group) } - it { should include(project1) } - it { should include(project2) } - it { should include(project3) } - it { should_not include(project4) } + it { is_expected.to include(project1) } + it { is_expected.to include(project2) } + it { is_expected.to include(project3) } + it { is_expected.not_to include(project4) } end context 'authenticated, group member' do @@ -43,9 +43,9 @@ describe ProjectsFinder do subject { ProjectsFinder.new.execute(user, group: group) } - it { should include(project1) } - it { should include(project2) } - it { should include(project3) } - it { should include(project4) } + it { is_expected.to include(project1) } + it { is_expected.to include(project2) } + it { is_expected.to include(project3) } + it { is_expected.to include(project4) } end end diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb index c645cbc964c..1b4ffc2d717 100644 --- a/spec/finders/snippets_finder_spec.rb +++ b/spec/finders/snippets_finder_spec.rb @@ -18,14 +18,14 @@ describe SnippetsFinder do it "returns all private and internal snippets" do snippets = SnippetsFinder.new.execute(user, filter: :all) - snippets.should include(@snippet2, @snippet3) - snippets.should_not include(@snippet1) + expect(snippets).to include(@snippet2, @snippet3) + expect(snippets).not_to include(@snippet1) end it "returns all public snippets" do snippets = SnippetsFinder.new.execute(nil, filter: :all) - snippets.should include(@snippet3) - snippets.should_not include(@snippet1, @snippet2) + expect(snippets).to include(@snippet3) + expect(snippets).not_to include(@snippet1, @snippet2) end end @@ -38,37 +38,37 @@ describe SnippetsFinder do it "returns all public and internal snippets" do snippets = SnippetsFinder.new.execute(user1, filter: :by_user, user: user) - snippets.should include(@snippet2, @snippet3) - snippets.should_not include(@snippet1) + expect(snippets).to include(@snippet2, @snippet3) + expect(snippets).not_to include(@snippet1) end it "returns internal snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_internal") - snippets.should include(@snippet2) - snippets.should_not include(@snippet1, @snippet3) + expect(snippets).to include(@snippet2) + expect(snippets).not_to include(@snippet1, @snippet3) end it "returns private snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_private") - snippets.should include(@snippet1) - snippets.should_not include(@snippet2, @snippet3) + expect(snippets).to include(@snippet1) + expect(snippets).not_to include(@snippet2, @snippet3) end it "returns public snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_public") - snippets.should include(@snippet3) - snippets.should_not include(@snippet1, @snippet2) + expect(snippets).to include(@snippet3) + expect(snippets).not_to include(@snippet1, @snippet2) end it "returns all snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user) - snippets.should include(@snippet1, @snippet2, @snippet3) + expect(snippets).to include(@snippet1, @snippet2, @snippet3) end it "returns only public snippets if unauthenticated user" do snippets = SnippetsFinder.new.execute(nil, filter: :by_user, user: user) - snippets.should include(@snippet3) - snippets.should_not include(@snippet2, @snippet1) + expect(snippets).to include(@snippet3) + expect(snippets).not_to include(@snippet2, @snippet1) end end @@ -82,20 +82,20 @@ describe SnippetsFinder do it "returns public snippets for unauthorized user" do snippets = SnippetsFinder.new.execute(nil, filter: :by_project, project: project1) - snippets.should include(@snippet3) - snippets.should_not include(@snippet1, @snippet2) + expect(snippets).to include(@snippet3) + expect(snippets).not_to include(@snippet1, @snippet2) end it "returns public and internal snippets for none project members" do snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1) - snippets.should include(@snippet2, @snippet3) - snippets.should_not include(@snippet1) + expect(snippets).to include(@snippet2, @snippet3) + expect(snippets).not_to include(@snippet1) end it "returns all snippets for project members" do project1.team << [user, :developer] snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1) - snippets.should include(@snippet1, @snippet2, @snippet3) + expect(snippets).to include(@snippet1, @snippet2, @snippet3) end end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index a46883b3c99..4c11709ed6e 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -3,20 +3,20 @@ require 'spec_helper' describe ApplicationHelper do describe 'current_controller?' do before do - controller.stub(:controller_name).and_return('foo') + allow(controller).to receive(:controller_name).and_return('foo') end it 'returns true when controller matches argument' do - current_controller?(:foo).should be_true + expect(current_controller?(:foo)).to be_truthy end it 'returns false when controller does not match argument' do - current_controller?(:bar).should_not be_true + expect(current_controller?(:bar)).not_to be_truthy end it 'should take any number of arguments' do - current_controller?(:baz, :bar).should_not be_true - current_controller?(:baz, :bar, :foo).should be_true + expect(current_controller?(:baz, :bar)).not_to be_truthy + expect(current_controller?(:baz, :bar, :foo)).to be_truthy end end @@ -26,33 +26,16 @@ describe ApplicationHelper do end it 'returns true when action matches argument' do - current_action?(:foo).should be_true + expect(current_action?(:foo)).to be_truthy end it 'returns false when action does not match argument' do - current_action?(:bar).should_not be_true + expect(current_action?(:bar)).not_to be_truthy end it 'should take any number of arguments' do - current_action?(:baz, :bar).should_not be_true - current_action?(:baz, :bar, :foo).should be_true - end - end - - describe 'group_icon' do - avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png') - - it 'should return an url for the avatar' do - group = create(:group) - group.avatar = File.open(avatar_file_path) - group.save! - group_icon(group.path).to_s.should match("/uploads/group/avatar/#{ group.id }/gitlab_logo.png") - end - - it 'should give default avatar_icon when no avatar is present' do - group = create(:group) - group.save! - group_icon(group.path).should match('group_avatar.png') + expect(current_action?(:baz, :bar)).not_to be_truthy + expect(current_action?(:baz, :bar, :foo)).to be_truthy end end @@ -63,18 +46,21 @@ describe ApplicationHelper do project = create(:project) project.avatar = File.open(avatar_file_path) project.save! - project_icon(project.to_param).to_s.should == - "<img alt=\"Gitlab logo\" src=\"/uploads/project/avatar/#{ project.id }/gitlab_logo.png\" />" + avatar_url = "http://localhost/uploads/project/avatar/#{ project.id }/gitlab_logo.png" + expect(project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s).to eq( + "<img alt=\"Gitlab logo\" src=\"#{avatar_url}\" />" + ) end it 'should give uploaded icon when present' do project = create(:project) project.save! - Project.any_instance.stub(:avatar_in_git).and_return(true) + allow_any_instance_of(Project).to receive(:avatar_in_git).and_return(true) - project_icon(project.to_param).to_s.should match( - image_tag(project_avatar_path(project))) + avatar_url = 'http://localhost' + namespace_project_avatar_path(project.namespace, project) + expect(project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s).to match( + image_tag(avatar_url)) end end @@ -85,7 +71,8 @@ describe ApplicationHelper do user = create(:user) user.avatar = File.open(avatar_file_path) user.save! - avatar_icon(user.email).to_s.should match("/uploads/user/avatar/#{ user.id }/gitlab_logo.png") + expect(avatar_icon(user.email).to_s). + to match("/uploads/user/avatar/#{ user.id }/gitlab_logo.png") end it 'should return an url for the avatar with relative url' do @@ -95,13 +82,14 @@ describe ApplicationHelper do user = create(:user) user.avatar = File.open(avatar_file_path) user.save! - avatar_icon(user.email).to_s.should match("/gitlab/uploads/user/avatar/#{ user.id }/gitlab_logo.png") + expect(avatar_icon(user.email).to_s). + to match("/gitlab/uploads/user/avatar/#{ user.id }/gitlab_logo.png") end it 'should call gravatar_icon when no avatar is present' do user = create(:user, email: 'test@example.com') user.save! - avatar_icon(user.email).to_s.should == 'http://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s=40&d=identicon' + expect(avatar_icon(user.email).to_s).to eq('http://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s=40&d=identicon') end end @@ -110,42 +98,47 @@ describe ApplicationHelper do it 'should return a generic avatar path when Gravatar is disabled' do ApplicationSetting.any_instance.stub(gravatar_enabled?: false) - gravatar_icon(user_email).should match('no_avatar.png') + expect(gravatar_icon(user_email)).to match('no_avatar.png') end it 'should return a generic avatar path when email is blank' do - gravatar_icon('').should match('no_avatar.png') + expect(gravatar_icon('')).to match('no_avatar.png') end it 'should return default gravatar url' do Gitlab.config.gitlab.stub(https: false) - gravatar_icon(user_email).should match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118') + url = 'http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118' + expect(gravatar_icon(user_email)).to match(url) end it 'should use SSL when appropriate' do Gitlab.config.gitlab.stub(https: true) - gravatar_icon(user_email).should match('https://secure.gravatar.com') + expect(gravatar_icon(user_email)).to match('https://secure.gravatar.com') end it 'should return custom gravatar path when gravatar_url is set' do allow(self).to receive(:request).and_return(double(:ssl? => false)) - Gitlab.config.gravatar.stub(:plain_url).and_return('http://example.local/?s=%{size}&hash=%{hash}') - gravatar_icon(user_email, 20).should == 'http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118' + allow(Gitlab.config.gravatar). + to receive(:plain_url). + and_return('http://example.local/?s=%{size}&hash=%{hash}') + url = 'http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118' + expect(gravatar_icon(user_email, 20)).to eq(url) end it 'should accept a custom size' do allow(self).to receive(:request).and_return(double(:ssl? => false)) - gravatar_icon(user_email, 64).should match(/\?s=64/) + expect(gravatar_icon(user_email, 64)).to match(/\?s=64/) end it 'should use default size when size is wrong' do allow(self).to receive(:request).and_return(double(:ssl? => false)) - gravatar_icon(user_email, nil).should match(/\?s=40/) + expect(gravatar_icon(user_email, nil)).to match(/\?s=40/) end it 'should be case insensitive' do allow(self).to receive(:request).and_return(double(:ssl? => false)) - gravatar_icon(user_email).should == gravatar_icon(user_email.upcase + ' ') + expect(gravatar_icon(user_email)). + to eq(gravatar_icon(user_email.upcase + ' ')) end end @@ -163,28 +156,32 @@ describe ApplicationHelper do end it 'includes a list of branch names' do - options[0][0].should == 'Branches' - options[0][1].should include('master', 'feature') + expect(options[0][0]).to eq('Branches') + expect(options[0][1]).to include('master', 'feature') end it 'includes a list of tag names' do - options[1][0].should == 'Tags' - options[1][1].should include('v1.0.0','v1.1.0') + expect(options[1][0]).to eq('Tags') + expect(options[1][1]).to include('v1.0.0', 'v1.1.0') end it 'includes a specific commit ref if defined' do # Must be an instance variable @ref = '2ed06dc41dbb5936af845b87d79e05bbf24c73b8' - options[2][0].should == 'Commit' - options[2][1].should == [@ref] + expect(options[2][0]).to eq('Commit') + expect(options[2][1]).to eq([@ref]) end it 'sorts tags in a natural order' do # Stub repository.tag_names to make sure we get some valid testing data - expect(@project.repository).to receive(:tag_names).and_return(['v1.0.9', 'v1.0.10', 'v2.0', 'v3.1.4.2', 'v1.0.9a']) + expect(@project.repository).to receive(:tag_names). + and_return(['v1.0.9', 'v1.0.10', 'v2.0', 'v3.1.4.2', 'v2.0rc1¿', + 'v1.0.9a', 'v2.0-rc1', 'v2.0rc2']) - options[1][1].should == ['v3.1.4.2', 'v2.0', 'v1.0.10', 'v1.0.9a', 'v1.0.9'] + expect(options[1][1]). + to eq(['v3.1.4.2', 'v2.0', 'v2.0rc2', 'v2.0rc1¿', 'v2.0-rc1', 'v1.0.10', + 'v1.0.9', 'v1.0.9a']) end end @@ -192,7 +189,7 @@ describe ApplicationHelper do context 'with current_user is nil' do it 'should return a string' do allow(self).to receive(:current_user).and_return(nil) - user_color_scheme_class.should be_kind_of(String) + expect(user_color_scheme_class).to be_kind_of(String) end end @@ -202,7 +199,7 @@ describe ApplicationHelper do it 'should return a string' do current_user = double(:color_scheme_id => color_scheme_id) allow(self).to receive(:current_user).and_return(current_user) - user_color_scheme_class.should be_kind_of(String) + expect(user_color_scheme_class).to be_kind_of(String) end end end @@ -213,17 +210,17 @@ describe ApplicationHelper do let(:a_tag) { '<a href="#">Foo</a>' } it 'allows the a tag' do - simple_sanitize(a_tag).should == a_tag + expect(simple_sanitize(a_tag)).to eq(a_tag) end it 'allows the span tag' do input = '<span class="foo">Bar</span>' - simple_sanitize(input).should == input + expect(simple_sanitize(input)).to eq(input) end it 'disallows other tags' do input = "<strike><b>#{a_tag}</b></strike>" - simple_sanitize(input).should == a_tag + expect(simple_sanitize(input)).to eq(a_tag) end end @@ -254,7 +251,7 @@ describe ApplicationHelper do let(:content) { 'Noël' } it 'should preserve encoding' do - content.encoding.name.should == 'UTF-8' + expect(content.encoding.name).to eq('UTF-8') expect(render_markup('foo.rst', content).encoding.name).to eq('UTF-8') end end diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb index 1338ce4873d..f6df12662bb 100644 --- a/spec/helpers/broadcast_messages_helper_spec.rb +++ b/spec/helpers/broadcast_messages_helper_spec.rb @@ -6,7 +6,7 @@ describe BroadcastMessagesHelper do context "default style" do it "should have no style" do - broadcast_styling(broadcast_message).should match('') + expect(broadcast_styling(broadcast_message)).to match('') end end @@ -14,7 +14,8 @@ describe BroadcastMessagesHelper do before { broadcast_message.stub(color: "#f2dede", font: "#b94a48") } it "should have a customized style" do - broadcast_styling(broadcast_message).should match('background-color:#f2dede;color:#b94a48') + expect(broadcast_styling(broadcast_message)). + to match('background-color:#f2dede;color:#b94a48') end end end diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index b07742a6ee2..5bd09793b11 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -10,58 +10,61 @@ describe DiffHelper do describe 'diff_hard_limit_enabled?' do it 'should return true if param is provided' do - controller.stub(:params).and_return { { :force_show_diff => true } } - diff_hard_limit_enabled?.should be_true + allow(controller).to receive(:params) { { force_show_diff: true } } + expect(diff_hard_limit_enabled?).to be_truthy end it 'should return false if param is not provided' do - diff_hard_limit_enabled?.should be_false + expect(diff_hard_limit_enabled?).to be_falsey end end describe 'allowed_diff_size' do it 'should return hard limit for a diff if force diff is true' do - controller.stub(:params).and_return { { :force_show_diff => true } } - allowed_diff_size.should eq(1000) + allow(controller).to receive(:params) { { force_show_diff: true } } + expect(allowed_diff_size).to eq(1000) end it 'should return safe limit for a diff if force diff is false' do - allowed_diff_size.should eq(100) + expect(allowed_diff_size).to eq(100) end end describe 'parallel_diff' do it 'should return an array of arrays containing the parsed diff' do - parallel_diff(diff_file, 0).should match_array(parallel_diff_result_array) + expect(parallel_diff(diff_file, 0)). + to match_array(parallel_diff_result_array) end end describe 'generate_line_code' do it 'should generate correct line code' do - generate_line_code(diff_file.file_path, diff_file.diff_lines.first).should == '2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6' + expect(generate_line_code(diff_file.file_path, diff_file.diff_lines.first)). + to eq('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6') end end describe 'unfold_bottom_class' do it 'should return empty string when bottom line shouldnt be unfolded' do - unfold_bottom_class(false).should == '' + expect(unfold_bottom_class(false)).to eq('') end it 'should return js class when bottom lines should be unfolded' do - unfold_bottom_class(true).should == 'js-unfold-bottom' + expect(unfold_bottom_class(true)).to eq('js-unfold-bottom') end end describe 'diff_line_content' do it 'should return non breaking space when line is empty' do - diff_line_content(nil).should eq(" ") + expect(diff_line_content(nil)).to eq(' ') end it 'should return the line itself' do - diff_line_content(diff_file.diff_lines.first.text).should eq("@@ -6,12 +6,18 @@ module Popen") - diff_line_content(diff_file.diff_lines.first.type).should eq("match") - diff_line_content(diff_file.diff_lines.first.new_pos).should eq(6) + expect(diff_line_content(diff_file.diff_lines.first.text)). + to eq('@@ -6,12 +6,18 @@ module Popen') + expect(diff_line_content(diff_file.diff_lines.first.type)).to eq('match') + expect(diff_line_content(diff_file.diff_lines.first.new_pos)).to eq(6) end end diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index c4a192ac1aa..b392371deb4 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -4,6 +4,8 @@ describe EventsHelper do include ApplicationHelper include GitlabMarkdownHelper + let(:current_user) { create(:user, email: "current@email.com") } + it 'should display one line of plain text without alteration' do input = 'A short, plain note' expect(event_note(input)).to match(input) @@ -50,4 +52,14 @@ describe EventsHelper do expect(event_note(input)).to match(link_url) expect(event_note(input)).to match(expected_link_text) end + + it 'should preserve code color scheme' do + input = "```ruby\ndef test\n 'hello world'\nend\n```" + expected = '<pre class="code highlight white ruby">' \ + "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \ + " <span class=\"s1\">\'hello world\'</span>\n" \ + "<span class=\"k\">end</span>\n" \ + '</code></pre>' + expect(event_note(input)).to eq(expected) + end end diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index d633287b2a9..fd80c615221 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -1,4 +1,4 @@ -require "spec_helper" +require 'spec_helper' describe GitlabMarkdownHelper do include ApplicationHelper @@ -9,6 +9,7 @@ describe GitlabMarkdownHelper do let(:user) { create(:user, username: 'gfm') } let(:commit) { project.repository.commit } + let(:earlier_commit){ project.repository.commit("HEAD~2") } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:snippet) { create(:project_snippet, project: project) } @@ -30,65 +31,113 @@ describe GitlabMarkdownHelper do it "should return unaltered text if project is nil" do actual = "Testing references: ##{issue.iid}" - gfm(actual).should_not == actual + expect(gfm(actual)).not_to eq(actual) @project = nil - gfm(actual).should == actual + expect(gfm(actual)).to eq(actual) end it "should not alter non-references" do actual = expected = "_Please_ *stop* 'helping' and all the other b*$#%' you do." - gfm(actual).should == expected + expect(gfm(actual)).to eq(expected) end it "should not touch HTML entities" do - @project.issues.stub(:where).with(id: '39').and_return([issue]) + allow(@project.issues).to receive(:where). + with(id: '39').and_return([issue]) actual = 'We'll accept good pull requests.' - gfm(actual).should == "We'll accept good pull requests." + expect(gfm(actual)).to eq("We'll accept good pull requests.") end it "should forward HTML options to links" do - gfm("Fixed in #{commit.id}", @project, class: 'foo'). - should have_selector('a.gfm.foo') + expect(gfm("Fixed in #{commit.id}", @project, class: 'foo')). + to have_selector('a.gfm.foo') + end + + describe "referencing a commit range" do + let(:expected) { namespace_project_compare_path(project.namespace, project, from: earlier_commit.id, to: commit.id) } + + it "should link using a full id" do + actual = "What happened in #{earlier_commit.id}...#{commit.id}" + expect(gfm(actual)).to match(expected) + end + + it "should link using a short id" do + actual = "What happened in #{earlier_commit.short_id}...#{commit.short_id}" + expected = namespace_project_compare_path(project.namespace, project, from: earlier_commit.short_id, to: commit.short_id) + expect(gfm(actual)).to match(expected) + end + + it "should link inclusively" do + actual = "What happened in #{earlier_commit.id}..#{commit.id}" + expected = namespace_project_compare_path(project.namespace, project, from: "#{earlier_commit.id}^", to: commit.id) + expect(gfm(actual)).to match(expected) + end + + it "should link with adjacent text" do + actual = "(see #{earlier_commit.id}...#{commit.id})" + expect(gfm(actual)).to match(expected) + end + + it "should keep whitespace intact" do + actual = "Changes #{earlier_commit.id}...#{commit.id} dramatically" + expected = /Changes <a.+>#{earlier_commit.id}...#{commit.id}<\/a> dramatically/ + expect(gfm(actual)).to match(expected) + end + + it "should not link with an invalid id" do + actual = expected = "What happened in #{earlier_commit.id.reverse}...#{commit.id.reverse}" + expect(gfm(actual)).to eq(expected) + end + + it "should include a title attribute" do + actual = "What happened in #{earlier_commit.id}...#{commit.id}" + expect(gfm(actual)).to match(/title="Commits #{earlier_commit.id} through #{commit.id}"/) + end + + it "should include standard gfm classes" do + actual = "What happened in #{earlier_commit.id}...#{commit.id}" + expect(gfm(actual)).to match(/class="\s?gfm gfm-commit_range\s?"/) + end end describe "referencing a commit" do - let(:expected) { project_commit_path(project, commit) } + let(:expected) { namespace_project_commit_path(project.namespace, project, commit) } it "should link using a full id" do actual = "Reverts #{commit.id}" - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link using a short id" do actual = "Backported from #{commit.short_id}" - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link with adjacent text" do actual = "Reverted (see #{commit.id})" - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should keep whitespace intact" do actual = "Changes #{commit.id} dramatically" expected = /Changes <a.+>#{commit.id}<\/a> dramatically/ - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should not link with an invalid id" do actual = expected = "What happened in #{commit.id.reverse}" - gfm(actual).should == expected + expect(gfm(actual)).to eq(expected) end it "should include a title attribute" do actual = "Reverts #{commit.id}" - gfm(actual).should match(/title="#{commit.link_title}"/) + expect(gfm(actual)).to match(/title="#{commit.link_title}"/) end it "should include standard gfm classes" do actual = "Reverts #{commit.id}" - gfm(actual).should match(/class="\s?gfm gfm-commit\s?"/) + expect(gfm(actual)).to match(/class="\s?gfm gfm-commit\s?"/) end end @@ -101,37 +150,37 @@ describe GitlabMarkdownHelper do end it "should link using a simple name" do - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link using a name with dots" do user.update_attributes(name: "alphA.Beta") - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link using name with underscores" do user.update_attributes(name: "ping_pong_king") - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link with adjacent text" do actual = "Mail the admin (@#{user.username})" - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should keep whitespace intact" do actual = "Yes, @#{user.username} is right." expected = /Yes, <a.+>@#{user.username}<\/a> is right/ - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should not link with an invalid id" do actual = expected = "@#{user.username.reverse} you are right." - gfm(actual).should == expected + expect(gfm(actual)).to eq(expected) end it "should include standard gfm classes" do - gfm(actual).should match(/class="\s?gfm gfm-team_member\s?"/) + expect(gfm(actual)).to match(/class="\s?gfm gfm-team_member\s?"/) end end @@ -145,40 +194,41 @@ describe GitlabMarkdownHelper do # Currently limited to Snippets, Issues and MergeRequests shared_examples 'referenced object' do let(:actual) { "Reference to #{reference}" } - let(:expected) { polymorphic_path([project, object]) } + let(:expected) { polymorphic_path([project.namespace, project, object]) } it "should link using a valid id" do - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link with adjacent text" do # Wrap the reference in parenthesis - gfm(actual.gsub(reference, "(#{reference})")).should match(expected) + expect(gfm(actual.gsub(reference, "(#{reference})"))).to match(expected) # Append some text to the end of the reference - gfm(actual.gsub(reference, "#{reference}, right?")).should match(expected) + expect(gfm(actual.gsub(reference, "#{reference}, right?"))). + to match(expected) end it "should keep whitespace intact" do actual = "Referenced #{reference} already." expected = /Referenced <a.+>[^\s]+<\/a> already/ - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should not link with an invalid id" do # Modify the reference string so it's still parsed, but is invalid reference.gsub!(/^(.)(\d+)$/, '\1' + ('\2' * 2)) - gfm(actual).should == actual + expect(gfm(actual)).to eq(actual) end it "should include a title attribute" do title = "#{object.class.to_s.titlecase}: #{object.title}" - gfm(actual).should match(/title="#{title}"/) + expect(gfm(actual)).to match(/title="#{title}"/) end it "should include standard gfm classes" do css = object.class.to_s.underscore - gfm(actual).should match(/class="\s?gfm gfm-#{css}\s?"/) + expect(gfm(actual)).to match(/class="\s?gfm gfm-#{css}\s?"/) end end @@ -197,34 +247,33 @@ describe GitlabMarkdownHelper do let(:actual) { "Reference to #{full_reference}" } let(:expected) do if object.is_a?(Commit) - project_commit_path(@other_project, object) + namespace_project_commit_path(@other_project.namespace, @other_project, object) else - polymorphic_path([@other_project, object]) + polymorphic_path([@other_project.namespace, @other_project, object]) end end it 'should link using a valid id' do - gfm(actual).should match( + expect(gfm(actual)).to match( /#{expected}.*#{Regexp.escape(full_reference)}/ ) end it 'should link with adjacent text' do # Wrap the reference in parenthesis - gfm(actual.gsub(full_reference, "(#{full_reference})")).should( + expect(gfm(actual.gsub(full_reference, "(#{full_reference})"))).to( match(expected) ) # Append some text to the end of the reference - gfm(actual.gsub(full_reference, "#{full_reference}, right?")).should( - match(expected) - ) + expect(gfm(actual.gsub(full_reference, "#{full_reference}, right?"))). + to(match(expected)) end it 'should keep whitespace intact' do actual = "Referenced #{full_reference} already." expected = /Referenced <a.+>[^\s]+<\/a> already/ - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it 'should not link with an invalid id' do @@ -234,7 +283,7 @@ describe GitlabMarkdownHelper do else reference.gsub!(/^(.)(\d+)$/, '\1' + ('\2' * 2)) end - gfm(actual).should == actual + expect(gfm(actual)).to eq(actual) end it 'should include a title attribute' do @@ -243,12 +292,12 @@ describe GitlabMarkdownHelper do else title = "#{object.class.to_s.titlecase}: #{object.title}" end - gfm(actual).should match(/title="#{title}"/) + expect(gfm(actual)).to match(/title="#{title}"/) end it 'should include standard gfm classes' do css = object.class.to_s.underscore - gfm(actual).should match(/class="\s?gfm gfm-#{css}\s?"/) + expect(gfm(actual)).to match(/class="\s?gfm gfm-#{css}\s?"/) end end @@ -307,36 +356,37 @@ describe GitlabMarkdownHelper do end it "should link using a valid id" do - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link with adjacent text" do # Wrap the reference in parenthesis - gfm(actual.gsub(reference, "(#{reference})")).should match(expected) + expect(gfm(actual.gsub(reference, "(#{reference})"))).to match(expected) # Append some text to the end of the reference - gfm(actual.gsub(reference, "#{reference}, right?")).should match(expected) + expect(gfm(actual.gsub(reference, "#{reference}, right?"))). + to match(expected) end it "should keep whitespace intact" do actual = "Referenced #{reference} already." expected = /Referenced <a.+>[^\s]+<\/a> already/ - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should not link with an invalid id" do # Modify the reference string so it's still parsed, but is invalid invalid_reference = actual.gsub(/(\d+)$/, "r45") - gfm(invalid_reference).should == invalid_reference + expect(gfm(invalid_reference)).to eq(invalid_reference) end it "should include a title attribute" do title = "Issue in JIRA tracker" - gfm(actual).should match(/title="#{title}"/) + expect(gfm(actual)).to match(/title="#{title}"/) end it "should include standard gfm classes" do - gfm(actual).should match(/class="\s?gfm gfm-issue\s?"/) + expect(gfm(actual)).to match(/class="\s?gfm gfm-issue\s?"/) end end @@ -351,40 +401,40 @@ describe GitlabMarkdownHelper do let(:object) { snippet } let(:reference) { "$#{snippet.id}" } let(:actual) { "Reference to #{reference}" } - let(:expected) { project_snippet_path(project, object) } + let(:expected) { namespace_project_snippet_path(project.namespace, project, object) } it "should link using a valid id" do - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should link with adjacent text" do # Wrap the reference in parenthesis - gfm(actual.gsub(reference, "(#{reference})")).should match(expected) + expect(gfm(actual.gsub(reference, "(#{reference})"))).to match(expected) # Append some text to the end of the reference - gfm(actual.gsub(reference, "#{reference}, right?")).should match(expected) + expect(gfm(actual.gsub(reference, "#{reference}, right?"))).to match(expected) end it "should keep whitespace intact" do actual = "Referenced #{reference} already." expected = /Referenced <a.+>[^\s]+<\/a> already/ - gfm(actual).should match(expected) + expect(gfm(actual)).to match(expected) end it "should not link with an invalid id" do # Modify the reference string so it's still parsed, but is invalid reference.gsub!(/^(.)(\d+)$/, '\1' + ('\2' * 2)) - gfm(actual).should == actual + expect(gfm(actual)).to eq(actual) end it "should include a title attribute" do title = "Snippet: #{object.title}" - gfm(actual).should match(/title="#{title}"/) + expect(gfm(actual)).to match(/title="#{title}"/) end it "should include standard gfm classes" do css = object.class.to_s.underscore - gfm(actual).should match(/class="\s?gfm gfm-snippet\s?"/) + expect(gfm(actual)).to match(/class="\s?gfm gfm-snippet\s?"/) end end @@ -393,70 +443,70 @@ describe GitlabMarkdownHelper do let(:actual) { "!#{merge_request.iid} -> #{commit.id} -> ##{issue.iid}" } it "should link to the merge request" do - expected = project_merge_request_path(project, merge_request) - gfm(actual).should match(expected) + expected = namespace_project_merge_request_path(project.namespace, project, merge_request) + expect(gfm(actual)).to match(expected) end it "should link to the commit" do - expected = project_commit_path(project, commit) - gfm(actual).should match(expected) + expected = namespace_project_commit_path(project.namespace, project, commit) + expect(gfm(actual)).to match(expected) end it "should link to the issue" do - expected = project_issue_path(project, issue) - gfm(actual).should match(expected) + expected = namespace_project_issue_path(project.namespace, project, issue) + expect(gfm(actual)).to match(expected) end end describe "emoji" do it "matches at the start of a string" do - gfm(":+1:").should match(/<img/) + expect(gfm(":+1:")).to match(/<img/) end it "matches at the end of a string" do - gfm("This gets a :-1:").should match(/<img/) + expect(gfm("This gets a :-1:")).to match(/<img/) end it "matches with adjacent text" do - gfm("+1 (:+1:)").should match(/<img/) + expect(gfm("+1 (:+1:)")).to match(/<img/) end it "has a title attribute" do - gfm(":-1:").should match(/title=":-1:"/) + expect(gfm(":-1:")).to match(/title=":-1:"/) end it "has an alt attribute" do - gfm(":-1:").should match(/alt=":-1:"/) + expect(gfm(":-1:")).to match(/alt=":-1:"/) end it "has an emoji class" do - gfm(":+1:").should match('class="emoji"') + expect(gfm(":+1:")).to match('class="emoji"') end it "sets height and width" do actual = gfm(":+1:") - actual.should match(/width="20"/) - actual.should match(/height="20"/) + expect(actual).to match(/width="20"/) + expect(actual).to match(/height="20"/) end it "keeps whitespace intact" do - gfm('This deserves a :+1: big time.'). - should match(/deserves a <img.+> big time/) + expect(gfm('This deserves a :+1: big time.')). + to match(/deserves a <img.+> big time/) end it "ignores invalid emoji" do - gfm(":invalid-emoji:").should_not match(/<img/) + expect(gfm(":invalid-emoji:")).not_to match(/<img/) end it "should work independent of reference links (i.e. without @project being set)" do @project = nil - gfm(":+1:").should match(/<img/) + expect(gfm(":+1:")).to match(/<img/) end end end describe "#link_to_gfm" do - let(:commit_path) { project_commit_path(project, commit) } + let(:commit_path) { namespace_project_commit_path(project.namespace, project, commit) } let(:issues) { create_list(:issue, 2, project: project) } it "should handle references nested in links with all the text" do @@ -467,60 +517,67 @@ describe GitlabMarkdownHelper do groups = actual.split("</a>") # Leading commit link - groups[0].should match(/href="#{commit_path}"/) - groups[0].should match(/This should finally fix $/) + expect(groups[0]).to match(/href="#{commit_path}"/) + expect(groups[0]).to match(/This should finally fix $/) # First issue link - groups[1].should match(/href="#{project_issue_url(project, issues[0])}"/) - groups[1].should match(/##{issues[0].iid}$/) + expect(groups[1]). + to match(/href="#{namespace_project_issue_path(project.namespace, project, issues[0])}"/) + expect(groups[1]).to match(/##{issues[0].iid}$/) # Internal commit link - groups[2].should match(/href="#{commit_path}"/) - groups[2].should match(/ and /) + expect(groups[2]).to match(/href="#{commit_path}"/) + expect(groups[2]).to match(/ and /) # Second issue link - groups[3].should match(/href="#{project_issue_url(project, issues[1])}"/) - groups[3].should match(/##{issues[1].iid}$/) + expect(groups[3]). + to match(/href="#{namespace_project_issue_path(project.namespace, project, issues[1])}"/) + expect(groups[3]).to match(/##{issues[1].iid}$/) # Trailing commit link - groups[4].should match(/href="#{commit_path}"/) - groups[4].should match(/ for real$/) + expect(groups[4]).to match(/href="#{commit_path}"/) + expect(groups[4]).to match(/ for real$/) end it "should forward HTML options" do actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo') - actual.should have_selector 'a.gfm.gfm-commit.foo' + expect(actual).to have_selector 'a.gfm.gfm-commit.foo' end it "escapes HTML passed in as the body" do actual = "This is a <h1>test</h1> - see ##{issues[0].iid}" - link_to_gfm(actual, commit_path).should match('<h1>test</h1>') + expect(link_to_gfm(actual, commit_path)). + to match('<h1>test</h1>') end end describe "#markdown" do it "should handle references in paragraphs" do actual = "\n\nLorem ipsum dolor sit amet. #{commit.id} Nam pulvinar sapien eget.\n" - expected = project_commit_path(project, commit) - markdown(actual).should match(expected) + expected = namespace_project_commit_path(project.namespace, project, commit) + expect(markdown(actual)).to match(expected) end it "should handle references in headers" do actual = "\n# Working around ##{issue.iid}\n## Apply !#{merge_request.iid}" - markdown(actual, {no_header_anchors:true}).should match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>}) - markdown(actual, {no_header_anchors:true}).should match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>}) + expect(markdown(actual, no_header_anchors: true)). + to match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>}) + expect(markdown(actual, no_header_anchors: true)). + to match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>}) end it "should add ids and links to headers" do # Test every rule except nested tags. text = '..Ab_c-d. e..' id = 'ab_c-d-e' - markdown("# #{text}").should match(%r{<h1 id="#{id}">#{text}<a href="[^"]*##{id}"></a></h1>}) - markdown("# #{text}", {no_header_anchors:true}).should == "<h1>#{text}</h1>" + expect(markdown("# #{text}")). + to match(%r{<h1 id="#{id}">#{text}<a href="[^"]*##{id}"></a></h1>}) + expect(markdown("# #{text}", {no_header_anchors:true})). + to eq("<h1>#{text}</h1>") id = 'link-text' - markdown("# [link text](url) ").should match( + expect(markdown("# [link text](url) ")).to match( %r{<h1 id="#{id}"><a href="[^"]*url">link text</a> <img[^>]*><a href="[^"]*##{id}"></a></h1>} ) end @@ -530,32 +587,37 @@ describe GitlabMarkdownHelper do actual = "\n* dark: ##{issue.iid}\n* light by @#{member.user.username}" - markdown(actual).should match(%r{<li>dark: <a.+>##{issue.iid}</a></li>}) - markdown(actual).should match(%r{<li>light by <a.+>@#{member.user.username}</a></li>}) + expect(markdown(actual)). + to match(%r{<li>dark: <a.+>##{issue.iid}</a></li>}) + expect(markdown(actual)). + to match(%r{<li>light by <a.+>@#{member.user.username}</a></li>}) end it "should not link the apostrophe to issue 39" do project.team << [user, :master] - project.issues.stub(:where).with(iid: '39').and_return([issue]) + allow(project.issues). + to receive(:where).with(iid: '39').and_return([issue]) actual = "Yes, it is @#{member.user.username}'s task." expected = /Yes, it is <a.+>@#{member.user.username}<\/a>'s task/ - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end it "should not link the apostrophe to issue 39 in code blocks" do project.team << [user, :master] - project.issues.stub(:where).with(iid: '39').and_return([issue]) + allow(project.issues). + to receive(:where).with(iid: '39').and_return([issue]) actual = "Yes, `it is @#{member.user.username}'s task.`" expected = /Yes, <code>it is @gfm\'s task.<\/code>/ - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end it "should handle references in <em>" do actual = "Apply _!#{merge_request.iid}_ ASAP" - markdown(actual).should match(%r{Apply <em><a.+>!#{merge_request.iid}</a></em>}) + expect(markdown(actual)). + to match(%r{Apply <em><a.+>!#{merge_request.iid}</a></em>}) end it "should handle tables" do @@ -564,91 +626,106 @@ describe GitlabMarkdownHelper do | cell 1 | cell 2 | | cell 3 | cell 4 |} - markdown(actual).should match(/\A<table/) + expect(markdown(actual)).to match(/\A<table/) end it "should leave code blocks untouched" do - helper.stub(:user_color_scheme_class).and_return(:white) + allow(helper).to receive(:user_color_scheme_class).and_return(:white) - target_html = "<pre class=\"code highlight white plaintext\"><code>some code from $40\nhere too\n</code></pre>\n" + target_html = "<pre class=\"code highlight white plaintext\"><code>some code from $#{snippet.id}\nhere too\n</code></pre>\n" - helper.markdown("\n some code from $#{snippet.id}\n here too\n").should == target_html - helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should == target_html + expect(helper.markdown("\n some code from $#{snippet.id}\n here too\n")). + to eq(target_html) + expect(helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n")). + to eq(target_html) end it "should leave inline code untouched" do - markdown("\nDon't use `$#{snippet.id}` here.\n").should == + expect(markdown("\nDon't use `$#{snippet.id}` here.\n")).to eq( "<p>Don't use <code>$#{snippet.id}</code> here.</p>\n" + ) end it "should leave ref-like autolinks untouched" do - markdown("look at http://example.tld/#!#{merge_request.iid}").should == "<p>look at <a href=\"http://example.tld/#!#{merge_request.iid}\">http://example.tld/#!#{merge_request.iid}</a></p>\n" + expect(markdown("look at http://example.tld/#!#{merge_request.iid}")).to eq("<p>look at <a href=\"http://example.tld/#!#{merge_request.iid}\">http://example.tld/#!#{merge_request.iid}</a></p>\n") end it "should leave ref-like href of 'manual' links untouched" do - markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})").should == "<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a class=\"gfm gfm-merge_request \" href=\"#{project_merge_request_url(project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n" + expect(markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})")).to eq("<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a class=\"gfm gfm-merge_request \" href=\"#{namespace_project_merge_request_url(project.namespace, project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n") end it "should leave ref-like src of images untouched" do - markdown("screen shot: ").should == "<p>screen shot: <img src=\"http://example.tld/#!#{merge_request.iid}\" alt=\"some image\"></p>\n" + expect(markdown("screen shot: ")).to eq("<p>screen shot: <img src=\"http://example.tld/#!#{merge_request.iid}\" alt=\"some image\"></p>\n") end it "should generate absolute urls for refs" do - markdown("##{issue.iid}").should include(project_issue_url(project, issue)) + expect(markdown("##{issue.iid}")).to include(namespace_project_issue_path(project.namespace, project, issue)) end it "should generate absolute urls for emoji" do - markdown(':smile:').should( - include(%(src="#{Gitlab.config.gitlab.url}/assets/emoji/smile.png)) + expect(markdown(':smile:')).to( + include(%(src="#{Gitlab.config.gitlab.url}/assets/emoji/#{Emoji.emoji_filename('smile')}.png)) ) end it "should generate absolute urls for emoji if relative url is present" do - Gitlab.config.gitlab.stub(:url).and_return('http://localhost/gitlab/root') - markdown(":smile:").should include("src=\"http://localhost/gitlab/root/assets/emoji/smile.png") + allow(Gitlab.config.gitlab).to receive(:url).and_return('http://localhost/gitlab/root') + expect(markdown(":smile:")).to include("src=\"http://localhost/gitlab/root/assets/emoji/#{Emoji.emoji_filename('smile')}.png") end it "should generate absolute urls for emoji if asset_host is present" do - Gitlab::Application.config.stub(:asset_host).and_return("https://cdn.example.com") + allow(Gitlab::Application.config).to receive(:asset_host).and_return("https://cdn.example.com") ActionView::Base.any_instance.stub_chain(:config, :asset_host).and_return("https://cdn.example.com") - markdown(":smile:").should include("src=\"https://cdn.example.com/assets/emoji/smile.png") + expect(markdown(":smile:")).to include("src=\"https://cdn.example.com/assets/emoji/#{Emoji.emoji_filename('smile')}.png") end it "should handle relative urls for a file in master" do actual = "[GitLab API doc](doc/api/README.md)\n" expected = "<p><a href=\"/#{project.path_with_namespace}/blob/#{@ref}/doc/api/README.md\">GitLab API doc</a></p>\n" - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) + end + + it "should handle relative urls for a file in master with an anchor" do + actual = "[GitLab API doc](doc/api/README.md#section)\n" + expected = "<p><a href=\"/#{project.path_with_namespace}/blob/#{@ref}/doc/api/README.md#section\">GitLab API doc</a></p>\n" + expect(markdown(actual)).to match(expected) + end + + it "should not handle relative urls for the current file with an anchor" do + actual = "[GitLab API doc](#section)\n" + expected = "<p><a href=\"#section\">GitLab API doc</a></p>\n" + expect(markdown(actual)).to match(expected) end it "should handle relative urls for a directory in master" do actual = "[GitLab API doc](doc/api)\n" expected = "<p><a href=\"/#{project.path_with_namespace}/tree/#{@ref}/doc/api\">GitLab API doc</a></p>\n" - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end it "should handle absolute urls" do actual = "[GitLab](https://www.gitlab.com)\n" expected = "<p><a href=\"https://www.gitlab.com\">GitLab</a></p>\n" - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end it "should handle relative urls in reference links for a file in master" do actual = "[GitLab API doc][GitLab readme]\n [GitLab readme]: doc/api/README.md\n" expected = "<p><a href=\"/#{project.path_with_namespace}/blob/#{@ref}/doc/api/README.md\">GitLab API doc</a></p>\n" - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end it "should handle relative urls in reference links for a directory in master" do actual = "[GitLab API doc directory][GitLab readmes]\n [GitLab readmes]: doc/api/\n" expected = "<p><a href=\"/#{project.path_with_namespace}/tree/#{@ref}/doc/api\">GitLab API doc directory</a></p>\n" - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end it "should not handle malformed relative urls in reference links for a file in master" do actual = "[GitLab readme]: doc/api/README.md\n" expected = "" - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end end @@ -661,29 +738,29 @@ describe GitlabMarkdownHelper do it "should not touch relative urls" do actual = "[GitLab API doc][GitLab readme]\n [GitLab readme]: doc/api/README.md\n" expected = "<p><a href=\"doc/api/README.md\">GitLab API doc</a></p>\n" - markdown(actual).should match(expected) + expect(markdown(actual)).to match(expected) end end describe "#render_wiki_content" do before do @wiki = double('WikiPage') - @wiki.stub(:content).and_return('wiki content') + allow(@wiki).to receive(:content).and_return('wiki content') end it "should use GitLab Flavored Markdown for markdown files" do - @wiki.stub(:format).and_return(:markdown) + allow(@wiki).to receive(:format).and_return(:markdown) - helper.should_receive(:markdown).with('wiki content') + expect(helper).to receive(:markdown).with('wiki content') helper.render_wiki_content(@wiki) end it "should use the Gollum renderer for all other file types" do - @wiki.stub(:format).and_return(:rdoc) + allow(@wiki).to receive(:format).and_return(:rdoc) formatted_content_stub = double('formatted_content') - formatted_content_stub.should_receive(:html_safe) - @wiki.stub(:formatted_content).and_return(formatted_content_stub) + expect(formatted_content_stub).to receive(:html_safe) + allow(@wiki).to receive(:formatted_content).and_return(formatted_content_stub) helper.render_wiki_content(@wiki) end diff --git a/spec/helpers/groups_helper.rb b/spec/helpers/groups_helper.rb new file mode 100644 index 00000000000..3e99ab84ec9 --- /dev/null +++ b/spec/helpers/groups_helper.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe GroupsHelper do + describe 'group_icon' do + avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png') + + it 'should return an url for the avatar' do + group = create(:group) + group.avatar = File.open(avatar_file_path) + group.save! + expect(group_icon(group.path).to_s). + to match("/uploads/group/avatar/#{ group.id }/gitlab_logo.png") + end + + it 'should give default avatar_icon when no avatar is present' do + group = create(:group) + group.save! + expect(group_icon(group.path)).to match('group_avatar.png') + end + end +end diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index c82729a52e2..54dd8d4aa64 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -5,132 +5,132 @@ describe IssuesHelper do let(:issue) { create :issue, project: project } let(:ext_project) { create :redmine_project } - describe :title_for_issue do + describe "title_for_issue" do it "should return issue title if used internal tracker" do @project = project - title_for_issue(issue.iid).should eq issue.title + expect(title_for_issue(issue.iid)).to eq issue.title end it "should always return empty string if used external tracker" do @project = ext_project - title_for_issue(rand(100)).should eq "" + expect(title_for_issue(rand(100))).to eq "" end it "should always return empty string if project nil" do @project = nil - title_for_issue(rand(100)).should eq "" + expect(title_for_issue(rand(100))).to eq "" end end - describe :url_for_project_issues do + describe "url_for_project_issues" do let(:project_url) { ext_project.external_issue_tracker.project_url } let(:ext_expected) do project_url.gsub(':project_id', ext_project.id.to_s) .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s) end - let(:int_expected) { polymorphic_path([project]) } + let(:int_expected) { polymorphic_path([@project.namespace, project]) } it "should return internal path if used internal tracker" do @project = project - url_for_project_issues.should match(int_expected) + expect(url_for_project_issues).to match(int_expected) end it "should return path to external tracker" do @project = ext_project - url_for_project_issues.should match(ext_expected) + expect(url_for_project_issues).to match(ext_expected) end it "should return empty string if project nil" do @project = nil - url_for_project_issues.should eq "" + expect(url_for_project_issues).to eq "" end describe "when external tracker was enabled and then config removed" do before do @project = ext_project - Gitlab.config.stub(:issues_tracker).and_return(nil) + allow(Gitlab.config).to receive(:issues_tracker).and_return(nil) end it "should return path to external tracker" do - url_for_project_issues.should match(ext_expected) + expect(url_for_project_issues).to match(ext_expected) end end end - describe :url_for_issue do + describe "url_for_issue" do let(:issues_url) { ext_project.external_issue_tracker.issues_url} let(:ext_expected) do issues_url.gsub(':id', issue.iid.to_s) .gsub(':project_id', ext_project.id.to_s) .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s) end - let(:int_expected) { polymorphic_path([project, issue]) } + let(:int_expected) { polymorphic_path([@project.namespace, project, issue]) } it "should return internal path if used internal tracker" do @project = project - url_for_issue(issue.iid).should match(int_expected) + expect(url_for_issue(issue.iid)).to match(int_expected) end it "should return path to external tracker" do @project = ext_project - url_for_issue(issue.iid).should match(ext_expected) + expect(url_for_issue(issue.iid)).to match(ext_expected) end it "should return empty string if project nil" do @project = nil - url_for_issue(issue.iid).should eq "" + expect(url_for_issue(issue.iid)).to eq "" end describe "when external tracker was enabled and then config removed" do before do @project = ext_project - Gitlab.config.stub(:issues_tracker).and_return(nil) + allow(Gitlab.config).to receive(:issues_tracker).and_return(nil) end it "should return external path" do - url_for_issue(issue.iid).should match(ext_expected) + expect(url_for_issue(issue.iid)).to match(ext_expected) end end end - describe :url_for_new_issue do + describe '#url_for_new_issue' do let(:issues_url) { ext_project.external_issue_tracker.new_issue_url } let(:ext_expected) do issues_url.gsub(':project_id', ext_project.id.to_s) .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s) end - let(:int_expected) { new_project_issue_path(project) } + let(:int_expected) { new_namespace_project_issue_path(project.namespace, project) } it "should return internal path if used internal tracker" do @project = project - url_for_new_issue.should match(int_expected) + expect(url_for_new_issue).to match(int_expected) end it "should return path to external tracker" do @project = ext_project - url_for_new_issue.should match(ext_expected) + expect(url_for_new_issue).to match(ext_expected) end it "should return empty string if project nil" do @project = nil - url_for_new_issue.should eq "" + expect(url_for_new_issue).to eq "" end describe "when external tracker was enabled and then config removed" do before do @project = ext_project - Gitlab.config.stub(:issues_tracker).and_return(nil) + allow(Gitlab.config).to receive(:issues_tracker).and_return(nil) end it "should return internal path" do - url_for_new_issue.should match(ext_expected) + expect(url_for_new_issue).to match(ext_expected) end end end diff --git a/spec/helpers/merge_requests_helper.rb b/spec/helpers/merge_requests_helper.rb index 5a317c4886b..5262d644048 100644 --- a/spec/helpers/merge_requests_helper.rb +++ b/spec/helpers/merge_requests_helper.rb @@ -7,6 +7,6 @@ describe MergeRequestsHelper do [build(:issue, iid: 1), build(:issue, iid: 2), build(:issue, iid: 3)] end - it { should eq('#1, #2, and #3') } + it { is_expected.to eq('#1, #2, and #3') } end end diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb index dcc3318e4f9..482cb33e94f 100644 --- a/spec/helpers/notifications_helper_spec.rb +++ b/spec/helpers/notifications_helper_spec.rb @@ -11,7 +11,7 @@ describe NotificationsHelper do before { notification.stub(disabled?: true) } it "has a red icon" do - notification_icon(notification).should match('class="fa fa-volume-off ns-mute"') + expect(notification_icon(notification)).to match('class="fa fa-volume-off ns-mute"') end end @@ -19,7 +19,7 @@ describe NotificationsHelper do before { notification.stub(participating?: true) } it "has a blue icon" do - notification_icon(notification).should match('class="fa fa-volume-down ns-part"') + expect(notification_icon(notification)).to match('class="fa fa-volume-down ns-part"') end end @@ -27,12 +27,12 @@ describe NotificationsHelper do before { notification.stub(watch?: true) } it "has a green icon" do - notification_icon(notification).should match('class="fa fa-volume-up ns-watch"') + expect(notification_icon(notification)).to match('class="fa fa-volume-up ns-watch"') end end it "has a blue icon" do - notification_icon(notification).should match('class="fa fa-circle-o ns-default"') + expect(notification_icon(notification)).to match('class="fa fa-circle-o ns-default"') end end end diff --git a/spec/helpers/oauth_helper_spec.rb b/spec/helpers/oauth_helper_spec.rb index 453699136e9..088c342fa13 100644 --- a/spec/helpers/oauth_helper_spec.rb +++ b/spec/helpers/oauth_helper_spec.rb @@ -4,17 +4,17 @@ describe OauthHelper do describe "additional_providers" do it 'returns all enabled providers' do allow(helper).to receive(:enabled_oauth_providers) { [:twitter, :github] } - helper.additional_providers.should include(*[:twitter, :github]) + expect(helper.additional_providers).to include(*[:twitter, :github]) end it 'does not return ldap provider' do allow(helper).to receive(:enabled_oauth_providers) { [:twitter, :ldapmain] } - helper.additional_providers.should include(:twitter) + expect(helper.additional_providers).to include(:twitter) end it 'returns empty array' do allow(helper).to receive(:enabled_oauth_providers) { [] } - helper.additional_providers.should == [] + expect(helper.additional_providers).to eq([]) end end end
\ No newline at end of file diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 281d4862199..0f78725e3d9 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' describe ProjectsHelper do describe "#project_status_css_class" do it "returns appropriate class" do - project_status_css_class("started").should == "active" - project_status_css_class("failed").should == "danger" - project_status_css_class("finished").should == "success" + expect(project_status_css_class("started")).to eq("active") + expect(project_status_css_class("failed")).to eq("danger") + expect(project_status_css_class("finished")).to eq("success") end end end diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 733f2754727..b327f4f911a 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -13,7 +13,7 @@ describe SearchHelper do end it "it returns nil" do - search_autocomplete_opts("q").should be_nil + expect(search_autocomplete_opts("q")).to be_nil end end @@ -25,29 +25,29 @@ describe SearchHelper do end it "includes Help sections" do - search_autocomplete_opts("hel").size.should == 9 + expect(search_autocomplete_opts("hel").size).to eq(9) end it "includes default sections" do - search_autocomplete_opts("adm").size.should == 1 + expect(search_autocomplete_opts("adm").size).to eq(1) end it "includes the user's groups" do create(:group).add_owner(user) - search_autocomplete_opts("gro").size.should == 1 + expect(search_autocomplete_opts("gro").size).to eq(1) end it "includes the user's projects" do project = create(:project, namespace: create(:namespace, owner: user)) - search_autocomplete_opts(project.name).size.should == 1 + expect(search_autocomplete_opts(project.name).size).to eq(1) end context "with a current project" do before { @project = create(:project) } it "includes project-specific sections" do - search_autocomplete_opts("Files").size.should == 1 - search_autocomplete_opts("Commits").size.should == 1 + expect(search_autocomplete_opts("Files").size).to eq(1) + expect(search_autocomplete_opts("Commits").size).to eq(1) end end end diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index 41c9f038c26..aef1108e333 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -19,28 +19,28 @@ describe SubmoduleHelper do Gitlab.config.gitlab_shell.stub(ssh_port: 22) # set this just to be sure Gitlab.config.gitlab_shell.stub(ssh_path_prefix: Settings.send(:build_gitlab_shell_ssh_path_prefix)) stub_url([ config.user, '@', config.host, ':gitlab-org/gitlab-ce.git' ].join('')) - submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ] + expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) end it 'should detect ssh on non-standard port' do Gitlab.config.gitlab_shell.stub(ssh_port: 2222) Gitlab.config.gitlab_shell.stub(ssh_path_prefix: Settings.send(:build_gitlab_shell_ssh_path_prefix)) stub_url([ 'ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git' ].join('')) - submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ] + expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) end it 'should detect http on standard port' do Gitlab.config.gitlab.stub(port: 80) Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url)) stub_url([ 'http://', config.host, '/gitlab-org/gitlab-ce.git' ].join('')) - submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ] + expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) end it 'should detect http on non-standard port' do Gitlab.config.gitlab.stub(port: 3000) Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url)) stub_url([ 'http://', config.host, ':3000/gitlab-org/gitlab-ce.git' ].join('')) - submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ] + expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) end it 'should work with relative_url_root' do @@ -48,67 +48,67 @@ describe SubmoduleHelper do Gitlab.config.gitlab.stub(relative_url_root: '/gitlab/root') Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url)) stub_url([ 'http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git' ].join('')) - submodule_links(submodule_item).should == [ project_path('gitlab-org/gitlab-ce'), project_tree_path('gitlab-org/gitlab-ce', 'hash') ] + expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) end end context 'submodule on github.com' do it 'should detect ssh' do stub_url('git@github.com:gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ] + expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) end it 'should detect http' do stub_url('http://github.com/gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ] + expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) end it 'should detect https' do stub_url('https://github.com/gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ] + expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) end it 'should return original with non-standard url' do stub_url('http://github.com/gitlab-org/gitlab-ce') - submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ] + expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) stub_url('http://github.com/another/gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ] + expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) end end context 'submodule on gitlab.com' do it 'should detect ssh' do stub_url('git@gitlab.com:gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ] + expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) end it 'should detect http' do stub_url('http://gitlab.com/gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ] + expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) end it 'should detect https' do stub_url('https://gitlab.com/gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ] + expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) end it 'should return original with non-standard url' do stub_url('http://gitlab.com/gitlab-org/gitlab-ce') - submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ] + expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) stub_url('http://gitlab.com/another/gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ] + expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) end end context 'submodule on unsupported' do it 'should return original' do stub_url('http://mygitserver.com/gitlab-org/gitlab-ce') - submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ] + expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) stub_url('http://mygitserver.com/gitlab-org/gitlab-ce.git') - submodule_links(submodule_item).should == [ repo.submodule_url_for, nil ] + expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) end end end diff --git a/spec/helpers/tab_helper_spec.rb b/spec/helpers/tab_helper_spec.rb index fa8a3f554f7..fc0ceecfbe7 100644 --- a/spec/helpers/tab_helper_spec.rb +++ b/spec/helpers/tab_helper_spec.rb @@ -5,40 +5,40 @@ describe TabHelper do describe 'nav_link' do before do - controller.stub(:controller_name).and_return('foo') + allow(controller).to receive(:controller_name).and_return('foo') allow(self).to receive(:action_name).and_return('foo') end it "captures block output" do - nav_link { "Testing Blocks" }.should match(/Testing Blocks/) + expect(nav_link { "Testing Blocks" }).to match(/Testing Blocks/) end it "performs checks on the current controller" do - nav_link(controller: :foo).should match(/<li class="active">/) - nav_link(controller: :bar).should_not match(/active/) - nav_link(controller: [:foo, :bar]).should match(/active/) + expect(nav_link(controller: :foo)).to match(/<li class="active">/) + expect(nav_link(controller: :bar)).not_to match(/active/) + expect(nav_link(controller: [:foo, :bar])).to match(/active/) end it "performs checks on the current action" do - nav_link(action: :foo).should match(/<li class="active">/) - nav_link(action: :bar).should_not match(/active/) - nav_link(action: [:foo, :bar]).should match(/active/) + expect(nav_link(action: :foo)).to match(/<li class="active">/) + expect(nav_link(action: :bar)).not_to match(/active/) + expect(nav_link(action: [:foo, :bar])).to match(/active/) end it "performs checks on both controller and action when both are present" do - nav_link(controller: :bar, action: :foo).should_not match(/active/) - nav_link(controller: :foo, action: :bar).should_not match(/active/) - nav_link(controller: :foo, action: :foo).should match(/active/) + expect(nav_link(controller: :bar, action: :foo)).not_to match(/active/) + expect(nav_link(controller: :foo, action: :bar)).not_to match(/active/) + expect(nav_link(controller: :foo, action: :foo)).to match(/active/) end it "accepts a path shorthand" do - nav_link(path: 'foo#bar').should_not match(/active/) - nav_link(path: 'foo#foo').should match(/active/) + expect(nav_link(path: 'foo#bar')).not_to match(/active/) + expect(nav_link(path: 'foo#foo')).to match(/active/) end it "passes extra html options to the list element" do - nav_link(action: :foo, html_options: {class: 'home'}).should match(/<li class="home active">/) - nav_link(html_options: {class: 'active'}).should match(/<li class="active">/) + expect(nav_link(action: :foo, html_options: {class: 'home'})).to match(/<li class="home active">/) + expect(nav_link(html_options: {class: 'active'})).to match(/<li class="active">/) end end end diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb index 8aa50c4c778..8271e00f41b 100644 --- a/spec/helpers/tree_helper_spec.rb +++ b/spec/helpers/tree_helper_spec.rb @@ -13,7 +13,7 @@ describe TreeHelper do let(:tree_item) { double(name: "files", path: "files") } it "should return the directory name" do - flatten_tree(tree_item).should match('files') + expect(flatten_tree(tree_item)).to match('files') end end @@ -21,7 +21,7 @@ describe TreeHelper do let(:tree_item) { double(name: "foo", path: "foo") } it "should return the flattened path" do - flatten_tree(tree_item).should match('foo/bar') + expect(flatten_tree(tree_item)).to match('foo/bar') end end end diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb index 8bf6ee2ed50..06d5450688b 100644 --- a/spec/lib/disable_email_interceptor_spec.rb +++ b/spec/lib/disable_email_interceptor_spec.rb @@ -6,7 +6,7 @@ describe DisableEmailInterceptor do end it 'should not send emails' do - Gitlab.config.gitlab.stub(:email_enabled).and_return(false) + allow(Gitlab.config.gitlab).to receive(:email_enabled).and_return(false) expect { deliver_mail }.not_to change(ActionMailer::Base.deliveries, :count) diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 7b3818ea5c8..ac602eac154 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -14,44 +14,46 @@ describe ExtractsPath do describe '#extract_ref' do it "returns an empty pair when no @project is set" do @project = nil - extract_ref('master/CHANGELOG').should == ['', ''] + expect(extract_ref('master/CHANGELOG')).to eq(['', '']) end context "without a path" do it "extracts a valid branch" do - extract_ref('master').should == ['master', ''] + expect(extract_ref('master')).to eq(['master', '']) end it "extracts a valid tag" do - extract_ref('v2.0.0').should == ['v2.0.0', ''] + expect(extract_ref('v2.0.0')).to eq(['v2.0.0', '']) end it "extracts a valid commit ref without a path" do - extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062').should == + expect(extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062')).to eq( ['f4b14494ef6abf3d144c28e4af0c20143383e062', ''] + ) end it "falls back to a primitive split for an invalid ref" do - extract_ref('stable').should == ['stable', ''] + expect(extract_ref('stable')).to eq(['stable', '']) end end context "with a path" do it "extracts a valid branch" do - extract_ref('foo/bar/baz/CHANGELOG').should == ['foo/bar/baz', 'CHANGELOG'] + expect(extract_ref('foo/bar/baz/CHANGELOG')).to eq(['foo/bar/baz', 'CHANGELOG']) end it "extracts a valid tag" do - extract_ref('v2.0.0/CHANGELOG').should == ['v2.0.0', 'CHANGELOG'] + expect(extract_ref('v2.0.0/CHANGELOG')).to eq(['v2.0.0', 'CHANGELOG']) end it "extracts a valid commit SHA" do - extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG').should == + expect(extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG')).to eq( ['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG'] + ) end it "falls back to a primitive split for an invalid ref" do - extract_ref('stable/CHANGELOG').should == ['stable', 'CHANGELOG'] + expect(extract_ref('stable/CHANGELOG')).to eq(['stable', 'CHANGELOG']) end end end diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/git_ref_validator_spec.rb index b2469c18395..4633b6f3934 100644 --- a/spec/lib/git_ref_validator_spec.rb +++ b/spec/lib/git_ref_validator_spec.rb @@ -1,20 +1,20 @@ require 'spec_helper' describe Gitlab::GitRefValidator do - it { Gitlab::GitRefValidator.validate('feature/new').should be_true } - it { Gitlab::GitRefValidator.validate('implement_@all').should be_true } - it { Gitlab::GitRefValidator.validate('my_new_feature').should be_true } - it { Gitlab::GitRefValidator.validate('#1').should be_true } - it { Gitlab::GitRefValidator.validate('feature/~new/').should be_false } - it { Gitlab::GitRefValidator.validate('feature/^new/').should be_false } - it { Gitlab::GitRefValidator.validate('feature/:new/').should be_false } - it { Gitlab::GitRefValidator.validate('feature/?new/').should be_false } - it { Gitlab::GitRefValidator.validate('feature/*new/').should be_false } - it { Gitlab::GitRefValidator.validate('feature/[new/').should be_false } - it { Gitlab::GitRefValidator.validate('feature/new/').should be_false } - it { Gitlab::GitRefValidator.validate('feature/new.').should be_false } - it { Gitlab::GitRefValidator.validate('feature\@{').should be_false } - it { Gitlab::GitRefValidator.validate('feature\new').should be_false } - it { Gitlab::GitRefValidator.validate('feature//new').should be_false } - it { Gitlab::GitRefValidator.validate('feature new').should be_false } + it { expect(Gitlab::GitRefValidator.validate('feature/new')).to be_truthy } + it { expect(Gitlab::GitRefValidator.validate('implement_@all')).to be_truthy } + it { expect(Gitlab::GitRefValidator.validate('my_new_feature')).to be_truthy } + it { expect(Gitlab::GitRefValidator.validate('#1')).to be_truthy } + it { expect(Gitlab::GitRefValidator.validate('feature/~new/')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature/^new/')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature/:new/')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature/?new/')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature/*new/')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature/[new/')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature/new/')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature/new.')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature\@{')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature\new')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature//new')).to be_falsey } + it { expect(Gitlab::GitRefValidator.validate('feature new')).to be_falsey } end diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb new file mode 100644 index 00000000000..768312f0028 --- /dev/null +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -0,0 +1,146 @@ +require "spec_helper" + +describe Grack::Auth do + let(:user) { create(:user) } + let(:project) { create(:project) } + + let(:app) { lambda { |env| [200, {}, "Success!"] } } + let!(:auth) { Grack::Auth.new(app) } + let(:env) { + { + "rack.input" => "", + "REQUEST_METHOD" => "GET", + "QUERY_STRING" => "service=git-upload-pack" + } + } + let(:status) { auth.call(env).first } + + describe "#call" do + context "when the project doesn't exist" do + before do + env["PATH_INFO"] = "doesnt/exist.git" + end + + context "when no authentication is provided" do + it "responds with status 401" do + expect(status).to eq(401) + end + end + + context "when username and password are provided" do + context "when authentication fails" do + before do + env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope") + end + + it "responds with status 401" do + expect(status).to eq(401) + end + end + + context "when authentication succeeds" do + before do + env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) + end + + it "responds with status 404" do + expect(status).to eq(404) + end + end + end + end + + context "when the project exists" do + before do + env["PATH_INFO"] = project.path_with_namespace + ".git" + end + + context "when the project is public" do + before do + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + it "responds with status 200" do + expect(status).to eq(200) + end + end + + context "when the project is private" do + before do + project.update_attribute(:visibility_level, Project::PRIVATE) + end + + context "when no authentication is provided" do + it "responds with status 401" do + expect(status).to eq(401) + end + end + + context "when username and password are provided" do + context "when authentication fails" do + before do + env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope") + end + + it "responds with status 401" do + expect(status).to eq(401) + end + end + + context "when authentication succeeds" do + before do + env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) + end + + context "when the user has access to the project" do + before do + project.team << [user, :master] + end + + context "when the user is blocked" do + before do + user.block + project.team << [user, :master] + end + + it "responds with status 404" do + expect(status).to eq(404) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + expect(status).to eq(200) + end + end + end + + context "when the user doesn't have access to the project" do + it "responds with status 404" do + expect(status).to eq(404) + end + end + end + end + + context "when a gitlab ci token is provided" do + let(:token) { "123" } + + before do + gitlab_ci_service = project.build_gitlab_ci_service + gitlab_ci_service.active = true + gitlab_ci_service.token = token + gitlab_ci_service.project_url = "http://google.com" + gitlab_ci_service.save + + env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) + end + + it "responds with status 200" do + expect(status).to eq(200) + end + end + end + end + end +end diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb index f00ec0fa401..27279465c1a 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/backend/shell_spec.rb @@ -8,11 +8,11 @@ describe Gitlab::Shell do Project.stub(find: project) end - it { should respond_to :add_key } - it { should respond_to :remove_key } - it { should respond_to :add_repository } - it { should respond_to :remove_repository } - it { should respond_to :fork_repository } + it { is_expected.to respond_to :add_key } + it { is_expected.to respond_to :remove_key } + it { is_expected.to respond_to :add_repository } + it { is_expected.to respond_to :remove_repository } + it { is_expected.to respond_to :fork_repository } - it { gitlab_shell.url_to_repo('diaspora').should == Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git" } + it { expect(gitlab_shell.url_to_repo('diaspora')).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git") } end diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb new file mode 100644 index 00000000000..f5523105848 --- /dev/null +++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Gitlab::BitbucketImport::ProjectCreator do + let(:user) { create(:user, bitbucket_access_token: "asdffg", bitbucket_access_token_secret: "sekret") } + let(:repo) { { + name: 'Vim', + slug: 'vim', + is_private: true, + owner: "asd"}.with_indifferent_access + } + let(:namespace){ create(:namespace) } + + it 'creates project' do + allow_any_instance_of(Project).to receive(:add_import_job) + + project_creator = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, user) + project = project_creator.execute + + expect(project.import_url).to eq("ssh://git@bitbucket.org/asd/vim.git") + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end +end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 0a1f3fa351d..c96ee78e5fd 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -9,122 +9,122 @@ describe Gitlab::ClosingIssueExtractor do context 'with a single reference' do it do message = "Awesome commit (Closes ##{iid1})" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Awesome commit (closes ##{iid1})" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Closed ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "closed ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Closing ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "closing ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Close ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "close ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Awesome commit (Fixes ##{iid1})" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Awesome commit (fixes ##{iid1})" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Fixed ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "fixed ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Fixing ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "fixing ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Fix ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "fix ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Awesome commit (Resolves ##{iid1})" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Awesome commit (resolves ##{iid1})" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Resolved ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "resolved ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Resolving ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "resolving ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "Resolve ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end it do message = "resolve ##{iid1}" - subject.closed_by_message_in_project(message, project).should == [issue] + expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) end end @@ -137,37 +137,37 @@ describe Gitlab::ClosingIssueExtractor do it 'fetches issues in single line message' do message = "Closes ##{iid1} and fix ##{iid2}" - subject.closed_by_message_in_project(message, project). - should == [issue, other_issue] + expect(subject.closed_by_message_in_project(message, project)). + to eq([issue, other_issue]) end it 'fetches comma-separated issues references in single line message' do message = "Closes ##{iid1}, closes ##{iid2}" - subject.closed_by_message_in_project(message, project). - should == [issue, other_issue] + expect(subject.closed_by_message_in_project(message, project)). + to eq([issue, other_issue]) end it 'fetches comma-separated issues numbers in single line message' do message = "Closes ##{iid1}, ##{iid2} and ##{iid3}" - subject.closed_by_message_in_project(message, project). - should == [issue, other_issue, third_issue] + expect(subject.closed_by_message_in_project(message, project)). + to eq([issue, other_issue, third_issue]) end it 'fetches issues in multi-line message' do message = "Awesome commit (closes ##{iid1})\nAlso fixes ##{iid2}" - subject.closed_by_message_in_project(message, project). - should == [issue, other_issue] + expect(subject.closed_by_message_in_project(message, project)). + to eq([issue, other_issue]) end it 'fetches issues in hybrid message' do message = "Awesome commit (closes ##{iid1})\n"\ "Also fixing issues ##{iid2}, ##{iid3} and #4" - subject.closed_by_message_in_project(message, project). - should == [issue, other_issue, third_issue] + expect(subject.closed_by_message_in_project(message, project)). + to eq([issue, other_issue, third_issue]) end end end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index cf0b5c282c1..40eb45e37ca 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -11,11 +11,11 @@ describe Gitlab::Diff::File do describe :diff_lines do let(:diff_lines) { diff_file.diff_lines } - it { diff_lines.size.should == 30 } - it { diff_lines.first.should be_kind_of(Gitlab::Diff::Line) } + it { expect(diff_lines.size).to eq(30) } + it { expect(diff_lines.first).to be_kind_of(Gitlab::Diff::Line) } end describe :mode_changed? do - it { diff_file.mode_changed?.should be_false } + it { expect(diff_file.mode_changed?).to be_falsey } end end diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb index 35b78260acd..918f6d0ead4 100644 --- a/spec/lib/gitlab/diff/parser_spec.rb +++ b/spec/lib/gitlab/diff/parser_spec.rb @@ -50,43 +50,43 @@ eos @lines = parser.parse(diff.lines) end - it { @lines.size.should == 30 } + it { expect(@lines.size).to eq(30) } describe 'lines' do describe 'first line' do let(:line) { @lines.first } - it { line.type.should == 'match' } - it { line.old_pos.should == 6 } - it { line.new_pos.should == 6 } - it { line.text.should == '@@ -6,12 +6,18 @@ module Popen' } + it { expect(line.type).to eq('match') } + it { expect(line.old_pos).to eq(6) } + it { expect(line.new_pos).to eq(6) } + it { expect(line.text).to eq('@@ -6,12 +6,18 @@ module Popen') } end describe 'removal line' do let(:line) { @lines[10] } - it { line.type.should == 'old' } - it { line.old_pos.should == 14 } - it { line.new_pos.should == 13 } - it { line.text.should == '- options = { chdir: path }' } + it { expect(line.type).to eq('old') } + it { expect(line.old_pos).to eq(14) } + it { expect(line.new_pos).to eq(13) } + it { expect(line.text).to eq('- options = { chdir: path }') } end describe 'addition line' do let(:line) { @lines[16] } - it { line.type.should == 'new' } - it { line.old_pos.should == 15 } - it { line.new_pos.should == 18 } - it { line.text.should == '+ options = {' } + it { expect(line.type).to eq('new') } + it { expect(line.old_pos).to eq(15) } + it { expect(line.new_pos).to eq(18) } + it { expect(line.text).to eq('+ options = {') } end describe 'unchanged line' do let(:line) { @lines.last } - it { line.type.should == nil } - it { line.old_pos.should == 24 } - it { line.new_pos.should == 31 } - it { line.text.should == ' @cmd_output << stderr.read' } + it { expect(line.type).to eq(nil) } + it { expect(line.old_pos).to eq(24) } + it { expect(line.new_pos).to eq(31) } + it { expect(line.text).to eq(' @cmd_output << stderr.read') } end end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index fbcaa405f8d..666398eedd4 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -9,17 +9,17 @@ describe Gitlab::GitAccess do describe 'push to none protected branch' do it "returns true if user is a master" do project.team << [user, :master] - Gitlab::GitAccess.can_push_to_branch?(user, project, "random_branch").should be_true + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, "random_branch")).to be_truthy end it "returns true if user is a developer" do project.team << [user, :developer] - Gitlab::GitAccess.can_push_to_branch?(user, project, "random_branch").should be_true + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, "random_branch")).to be_truthy end it "returns false if user is a reporter" do project.team << [user, :reporter] - Gitlab::GitAccess.can_push_to_branch?(user, project, "random_branch").should be_false + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, "random_branch")).to be_falsey end end @@ -30,17 +30,17 @@ describe Gitlab::GitAccess do it "returns true if user is a master" do project.team << [user, :master] - Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name).should be_true + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name)).to be_truthy end it "returns false if user is a developer" do project.team << [user, :developer] - Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name).should be_false + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name)).to be_falsey end it "returns false if user is a reporter" do project.team << [user, :reporter] - Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name).should be_false + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name)).to be_falsey end end @@ -51,17 +51,17 @@ describe Gitlab::GitAccess do it "returns true if user is a master" do project.team << [user, :master] - Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name).should be_true + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name)).to be_truthy end it "returns true if user is a developer" do project.team << [user, :developer] - Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name).should be_true + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name)).to be_truthy end it "returns false if user is a reporter" do project.team << [user, :reporter] - Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name).should be_false + expect(Gitlab::GitAccess.can_push_to_branch?(user, project, @branch.name)).to be_falsey end end @@ -74,7 +74,7 @@ describe Gitlab::GitAccess do context 'pull code' do subject { access.download_access_check(user, project) } - it { subject.allowed?.should be_true } + it { expect(subject.allowed?).to be_truthy } end end @@ -84,7 +84,7 @@ describe Gitlab::GitAccess do context 'pull code' do subject { access.download_access_check(user, project) } - it { subject.allowed?.should be_false } + it { expect(subject.allowed?).to be_falsey } end end @@ -97,7 +97,7 @@ describe Gitlab::GitAccess do context 'pull code' do subject { access.download_access_check(user, project) } - it { subject.allowed?.should be_false } + it { expect(subject.allowed?).to be_falsey } end end @@ -105,7 +105,7 @@ describe Gitlab::GitAccess do context 'pull code' do subject { access.download_access_check(user, project) } - it { subject.allowed?.should be_false } + it { expect(subject.allowed?).to be_falsey } end end @@ -117,13 +117,13 @@ describe Gitlab::GitAccess do before { key.projects << project } subject { access.download_access_check(key, project) } - it { subject.allowed?.should be_true } + it { expect(subject.allowed?).to be_truthy } end context 'denied' do subject { access.download_access_check(key, project) } - it { subject.allowed?.should be_false } + it { expect(subject.allowed?).to be_falsey } end end end @@ -207,7 +207,7 @@ describe Gitlab::GitAccess do context action do subject { access.push_access_check(user, project, changes[action]) } - it { subject.allowed?.should allowed ? be_true : be_false } + it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey } end end end @@ -223,7 +223,7 @@ describe Gitlab::GitAccess do context action do subject { access.push_access_check(user, project, changes[action]) } - it { subject.allowed?.should allowed ? be_true : be_false } + it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey } end end end diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index 4ff45c0c616..c31c6764091 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -13,7 +13,7 @@ describe Gitlab::GitAccessWiki do subject { access.push_access_check(user, project, changes) } - it { subject.allowed?.should be_true } + it { expect(subject.allowed?).to be_truthy } end def changes diff --git a/spec/lib/gitlab/github/project_creator.rb b/spec/lib/gitlab/github/project_creator.rb deleted file mode 100644 index 0bade5619a5..00000000000 --- a/spec/lib/gitlab/github/project_creator.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Github::ProjectCreator do - let(:user) { create(:user, github_access_token: "asdffg") } - let(:repo) { OpenStruct.new( - login: 'vim', - name: 'vim', - private: true, - full_name: 'asd/vim', - clone_url: "https://gitlab.com/asd/vim.git", - owner: OpenStruct.new(login: "john")) - } - let(:namespace){ create(:namespace) } - - it 'creates project' do - Project.any_instance.stub(:add_import_job) - - project_creator = Gitlab::Github::ProjectCreator.new(repo, namespace, user) - project_creator.execute - project = Project.last - - project.import_url.should == "https://asdffg@gitlab.com/asd/vim.git" - project.visibility_level.should == Gitlab::VisibilityLevel::PRIVATE - end -end diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb new file mode 100644 index 00000000000..8d594a112d4 --- /dev/null +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::ProjectCreator do + let(:user) { create(:user, github_access_token: "asdffg") } + let(:repo) { OpenStruct.new( + login: 'vim', + name: 'vim', + private: true, + full_name: 'asd/vim', + clone_url: "https://gitlab.com/asd/vim.git", + owner: OpenStruct.new(login: "john")) + } + let(:namespace){ create(:namespace) } + + it 'creates project' do + allow_any_instance_of(Project).to receive(:add_import_job) + + project_creator = Gitlab::GithubImport::ProjectCreator.new(repo, namespace, user) + project = project_creator.execute + + expect(project.import_url).to eq("https://asdffg@gitlab.com/asd/vim.git") + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end +end diff --git a/spec/lib/gitlab/gitlab_import/project_creator.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb index 51f3534ed60..4c0d64ed138 100644 --- a/spec/lib/gitlab/gitlab_import/project_creator.rb +++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::GitlabImport::ProjectCreator do let(:user) { create(:user, gitlab_access_token: "asdffg") } - let(:repo) {{ + let(:repo) { { name: 'vim', path: 'vim', visibility_level: Gitlab::VisibilityLevel::PRIVATE, @@ -13,13 +13,12 @@ describe Gitlab::GitlabImport::ProjectCreator do let(:namespace){ create(:namespace) } it 'creates project' do - Project.any_instance.stub(:add_import_job) + allow_any_instance_of(Project).to receive(:add_import_job) project_creator = Gitlab::GitlabImport::ProjectCreator.new(repo, namespace, user) - project_creator.execute - project = Project.last + project = project_creator.execute - project.import_url.should == "https://oauth2:asdffg@gitlab.com/asd/vim.git" - project.visibility_level.should == Gitlab::VisibilityLevel::PRIVATE + expect(project.import_url).to eq("https://oauth2:asdffg@gitlab.com/asd/vim.git") + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) end end diff --git a/spec/lib/gitlab/gitlab_markdown_helper_spec.rb b/spec/lib/gitlab/gitlab_markdown_helper_spec.rb index 540618a5603..ab613193f41 100644 --- a/spec/lib/gitlab/gitlab_markdown_helper_spec.rb +++ b/spec/lib/gitlab/gitlab_markdown_helper_spec.rb @@ -5,24 +5,24 @@ describe Gitlab::MarkdownHelper do %w(textile rdoc org creole wiki mediawiki rst adoc asciidoc asc).each do |type| it "returns true for #{type} files" do - Gitlab::MarkdownHelper.markup?("README.#{type}").should be_true + expect(Gitlab::MarkdownHelper.markup?("README.#{type}")).to be_truthy end end it 'returns false when given a non-markup filename' do - Gitlab::MarkdownHelper.markup?('README.rb').should_not be_true + expect(Gitlab::MarkdownHelper.markup?('README.rb')).not_to be_truthy end end describe '#gitlab_markdown?' do %w(mdown md markdown).each do |type| it "returns true for #{type} files" do - Gitlab::MarkdownHelper.gitlab_markdown?("README.#{type}").should be_true + expect(Gitlab::MarkdownHelper.gitlab_markdown?("README.#{type}")).to be_truthy end end it 'returns false when given a non-markdown filename' do - Gitlab::MarkdownHelper.gitlab_markdown?('README.rb').should_not be_true + expect(Gitlab::MarkdownHelper.gitlab_markdown?('README.rb')).not_to be_truthy end end end diff --git a/spec/lib/gitlab/gitorious_import/project_creator.rb b/spec/lib/gitlab/gitorious_import/project_creator.rb new file mode 100644 index 00000000000..cf2318bb3a2 --- /dev/null +++ b/spec/lib/gitlab/gitorious_import/project_creator.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Gitlab::GitoriousImport::ProjectCreator do + let(:user) { create(:user) } + let(:repo) { Gitlab::GitoriousImport::Repository.new('foo/bar-baz-qux') } + let(:namespace){ create(:namespace) } + + it 'creates project' do + allow_any_instance_of(Project).to receive(:add_import_job) + + project_creator = Gitlab::GitoriousImport::ProjectCreator.new(repo, namespace, user) + project_creator.execute + project = Project.last + + expect(project.name).to eq("Bar Baz Qux") + expect(project.path).to eq("bar-baz-qux") + expect(project.namespace).to eq(namespace) + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) + expect(project.import_type).to eq("gitorious") + expect(project.import_source).to eq("foo/bar-baz-qux") + expect(project.import_url).to eq("https://gitorious.org/foo/bar-baz-qux.git") + end +end diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb index 4573b8696c4..39d46efcbc3 100644 --- a/spec/lib/gitlab/ldap/access_spec.rb +++ b/spec/lib/gitlab/ldap/access_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::LDAP::Access do context 'when the user cannot be found' do before { Gitlab::LDAP::Person.stub(find_by_dn: nil) } - it { should be_false } + it { is_expected.to be_falsey } end context 'when the user is found' do @@ -19,13 +19,18 @@ describe Gitlab::LDAP::Access do context 'and the user is diabled via active directory' do before { Gitlab::LDAP::Person.stub(disabled_via_active_directory?: true) } - it { should be_false } + it { is_expected.to be_falsey } + + it "should block user in GitLab" do + access.allowed? + user.should be_blocked + end end context 'and has no disabled flag in active diretory' do before { Gitlab::LDAP::Person.stub(disabled_via_active_directory?: false) } - it { should be_true } + it { is_expected.to be_truthy } end context 'without ActiveDirectory enabled' do @@ -34,8 +39,8 @@ describe Gitlab::LDAP::Access do Gitlab::LDAP::Config.any_instance.stub(active_directory: false) end - it { should be_true } + it { is_expected.to be_truthy } end end end -end
\ No newline at end of file +end diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb index 19347e47378..b609e4b38f2 100644 --- a/spec/lib/gitlab/ldap/adapter_spec.rb +++ b/spec/lib/gitlab/ldap/adapter_spec.rb @@ -12,20 +12,20 @@ describe Gitlab::LDAP::Adapter do context "and the result is non-empty" do before { ldap.stub(search: [:foo]) } - it { should be_true } + it { is_expected.to be_truthy } end context "and the result is empty" do before { ldap.stub(search: []) } - it { should be_false } + it { is_expected.to be_falsey } end end context "when the search encounters an error" do before { ldap.stub(search: nil, get_operation_result: double(code: 1, message: 'some error')) } - it { should be_false } + it { is_expected.to be_falsey } end end end diff --git a/spec/lib/gitlab/ldap/authentication_spec.rb b/spec/lib/gitlab/ldap/authentication_spec.rb index 11fdf108756..8afc2b21f46 100644 --- a/spec/lib/gitlab/ldap/authentication_spec.rb +++ b/spec/lib/gitlab/ldap/authentication_spec.rb @@ -19,7 +19,7 @@ describe Gitlab::LDAP::Authentication do klass.any_instance.stub(adapter: double(:adapter, bind_as: double(:ldap_user, dn: dn) )) - expect(klass.login(login, password)).to be_true + expect(klass.login(login, password)).to be_truthy end it "is false if the user does not exist" do @@ -27,27 +27,27 @@ describe Gitlab::LDAP::Authentication do klass.any_instance.stub(adapter: double(:adapter, bind_as: double(:ldap_user, dn: dn) )) - expect(klass.login(login, password)).to be_false + expect(klass.login(login, password)).to be_falsey end it "is false if authentication fails" do user # try only to fake the LDAP call klass.any_instance.stub(adapter: double(:adapter, bind_as: nil)) - expect(klass.login(login, password)).to be_false + expect(klass.login(login, password)).to be_falsey end it "fails if ldap is disabled" do Gitlab::LDAP::Config.stub(enabled?: false) - expect(klass.login(login, password)).to be_false + expect(klass.login(login, password)).to be_falsey end it "fails if no login is supplied" do - expect(klass.login('', password)).to be_false + expect(klass.login('', password)).to be_falsey end it "fails if no password is supplied" do - expect(klass.login(login, '')).to be_false + expect(klass.login(login, '')).to be_falsey end end end
\ No newline at end of file diff --git a/spec/lib/gitlab/ldap/config_spec.rb b/spec/lib/gitlab/ldap/config_spec.rb index 3ebb8aae243..2df2beca7a6 100644 --- a/spec/lib/gitlab/ldap/config_spec.rb +++ b/spec/lib/gitlab/ldap/config_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::LDAP::Config do let(:config) { Gitlab::LDAP::Config.new provider } let(:provider) { 'ldapmain' } - describe :initalize do + describe '#initalize' do it 'requires a provider' do expect{ Gitlab::LDAP::Config.new }.to raise_error ArgumentError end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 63ffc21ba3b..4f93545feb6 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -16,17 +16,17 @@ describe Gitlab::LDAP::User do describe :changed? do it "marks existing ldap user as changed" do existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain') - expect(gl_user.changed?).to be_true + expect(gl_user.changed?).to be_truthy end it "marks existing non-ldap user if the email matches as changed" do existing_user = create(:user, email: 'john@example.com') - expect(gl_user.changed?).to be_true + expect(gl_user.changed?).to be_truthy end it "dont marks existing ldap user as changed" do existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain') - expect(gl_user.changed?).to be_false + expect(gl_user.changed?).to be_falsey end end diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb new file mode 100644 index 00000000000..448cd0c6880 --- /dev/null +++ b/spec/lib/gitlab/note_data_builder_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe 'Gitlab::NoteDataBuilder' do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:data) { Gitlab::NoteDataBuilder.build(note, user) } + let(:note_url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors + + before(:each) do + expect(data).to have_key(:object_attributes) + expect(data[:object_attributes]).to have_key(:url) + expect(data[:object_attributes][:url]).to eq(note_url) + expect(data[:object_kind]).to eq('note') + expect(data[:user]).to eq(user.hook_attrs) + end + + describe 'When asking for a note on commit' do + let(:note) { create(:note_on_commit) } + + it 'returns the note and commit-specific data' do + expect(data).to have_key(:commit) + end + end + + describe 'When asking for a note on commit diff' do + let(:note) { create(:note_on_commit_diff) } + + it 'returns the note and commit-specific data' do + expect(data).to have_key(:commit) + end + end + + describe 'When asking for a note on issue' do + let(:issue) { create(:issue, created_at: fixed_time, updated_at: fixed_time) } + let(:note) { create(:note_on_issue, noteable_id: issue.id) } + + it 'returns the note and issue-specific data' do + expect(data).to have_key(:issue) + expect(data[:issue]).to eq(issue.hook_attrs) + end + end + + describe 'When asking for a note on merge request' do + let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) } + let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) } + + it 'returns the note and merge request data' do + expect(data).to have_key(:merge_request) + expect(data[:merge_request]).to eq(merge_request.hook_attrs) + end + end + + describe 'When asking for a note on merge request diff' do + let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) } + let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) } + + it 'returns the note and merge request diff data' do + expect(data).to have_key(:merge_request) + expect(data[:merge_request]).to eq(merge_request.hook_attrs) + end + end + + describe 'When asking for a note on project snippet' do + let!(:snippet) { create(:project_snippet, created_at: fixed_time, updated_at: fixed_time) } + let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) } + + it 'returns the note and project snippet data' do + expect(data).to have_key(:snippet) + expect(data[:snippet]).to eq(snippet.hook_attrs) + end + end +end diff --git a/spec/lib/gitlab/oauth/user_spec.rb b/spec/lib/gitlab/oauth/user_spec.rb index 88307515789..44cdd1e4fab 100644 --- a/spec/lib/gitlab/oauth/user_spec.rb +++ b/spec/lib/gitlab/oauth/user_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::OAuth::User do let(:auth_hash) { double(uid: uid, provider: provider, info: double(info_hash)) } let(:info_hash) do { - nickname: 'john', + nickname: '-john+gitlab-ETC%.git@gmail.com', name: 'John', email: 'john@mail.com' } @@ -19,12 +19,12 @@ describe Gitlab::OAuth::User do it "finds an existing user based on uid and provider (facebook)" do auth = double(info: double(name: 'John'), uid: 'my-uid', provider: 'my-provider') - expect( oauth_user.persisted? ).to be_true + expect( oauth_user.persisted? ).to be_truthy end it "returns false if use is not found in database" do auth_hash.stub(uid: 'non-existing') - expect( oauth_user.persisted? ).to be_false + expect( oauth_user.persisted? ).to be_falsey end end @@ -62,8 +62,8 @@ describe Gitlab::OAuth::User do it do oauth_user.save - gl_user.should be_valid - gl_user.should_not be_blocked + expect(gl_user).to be_valid + expect(gl_user).not_to be_blocked end end @@ -72,8 +72,8 @@ describe Gitlab::OAuth::User do it do oauth_user.save - gl_user.should be_valid - gl_user.should be_blocked + expect(gl_user).to be_valid + expect(gl_user).to be_blocked end end end @@ -89,8 +89,8 @@ describe Gitlab::OAuth::User do it do oauth_user.save - gl_user.should be_valid - gl_user.should_not be_blocked + expect(gl_user).to be_valid + expect(gl_user).not_to be_blocked end end @@ -99,8 +99,8 @@ describe Gitlab::OAuth::User do it do oauth_user.save - gl_user.should be_valid - gl_user.should_not be_blocked + expect(gl_user).to be_valid + expect(gl_user).not_to be_blocked end end end diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb index 76d506eb3c0..cd9d0456b25 100644 --- a/spec/lib/gitlab/popen_spec.rb +++ b/spec/lib/gitlab/popen_spec.rb @@ -13,8 +13,8 @@ describe 'Gitlab::Popen', no_db: true do @output, @status = @klass.new.popen(%W(ls), path) end - it { @status.should be_zero } - it { @output.should include('cache') } + it { expect(@status).to be_zero } + it { expect(@output).to include('cache') } end context 'non-zero status' do @@ -22,8 +22,8 @@ describe 'Gitlab::Popen', no_db: true do @output, @status = @klass.new.popen(%W(cat NOTHING), path) end - it { @status.should == 1 } - it { @output.should include('No such file or directory') } + it { expect(@status).to eq(1) } + it { expect(@output).to include('No such file or directory') } end context 'unsafe string command' do @@ -37,8 +37,8 @@ describe 'Gitlab::Popen', no_db: true do @output, @status = @klass.new.popen(%W(ls)) end - it { @status.should be_zero } - it { @output.should include('spec') } + it { expect(@status).to be_zero } + it { expect(@output).to include('spec') } end end diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb index 691fd133637..1b8ba7b4d43 100644 --- a/spec/lib/gitlab/push_data_builder_spec.rb +++ b/spec/lib/gitlab/push_data_builder_spec.rb @@ -8,12 +8,15 @@ describe 'Gitlab::PushDataBuilder' do describe :build_sample do let(:data) { Gitlab::PushDataBuilder.build_sample(project, user) } - it { data.should be_a(Hash) } - it { data[:before].should == '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' } - it { data[:after].should == '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } - it { data[:ref].should == 'refs/heads/master' } - it { data[:commits].size.should == 3 } - it { data[:total_commits_count].should == 3 } + it { expect(data).to be_a(Hash) } + it { expect(data[:before]).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } + it { expect(data[:after]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + it { expect(data[:ref]).to eq('refs/heads/master') } + it { expect(data[:commits].size).to eq(3) } + it { expect(data[:repository][:git_http_url]).to eq(project.http_url_to_repo) } + it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) } + it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) } + it { expect(data[:total_commits_count]).to eq(3) } end describe :build do @@ -25,12 +28,12 @@ describe 'Gitlab::PushDataBuilder' do 'refs/tags/v1.1.0') end - it { data.should be_a(Hash) } - it { data[:before].should == Gitlab::Git::BLANK_SHA } - it { data[:checkout_sha].should == '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } - it { data[:after].should == '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b' } - it { data[:ref].should == 'refs/tags/v1.1.0' } - it { data[:commits].should be_empty } - it { data[:total_commits_count].should be_zero } + it { expect(data).to be_a(Hash) } + it { expect(data[:before]).to eq(Gitlab::Git::BLANK_SHA) } + it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') } + it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } + it { expect(data[:commits]).to be_empty } + it { expect(data[:total_commits_count]).to be_zero } end end diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 5f45df4e8c3..034f8ee7c45 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -3,51 +3,56 @@ require 'spec_helper' describe Gitlab::ReferenceExtractor do it 'extracts username references' do subject.analyze('this contains a @user reference', nil) - subject.users.should == [{ project: nil, id: 'user' }] + expect(subject.users).to eq([{ project: nil, id: 'user' }]) end it 'extracts issue references' do subject.analyze('this one talks about issue #1234', nil) - subject.issues.should == [{ project: nil, id: '1234' }] + expect(subject.issues).to eq([{ project: nil, id: '1234' }]) end it 'extracts JIRA issue references' do subject.analyze('this one talks about issue JIRA-1234', nil) - subject.issues.should == [{ project: nil, id: 'JIRA-1234' }] + expect(subject.issues).to eq([{ project: nil, id: 'JIRA-1234' }]) end it 'extracts merge request references' do subject.analyze("and here's !43, a merge request", nil) - subject.merge_requests.should == [{ project: nil, id: '43' }] + expect(subject.merge_requests).to eq([{ project: nil, id: '43' }]) end it 'extracts snippet ids' do subject.analyze('snippets like $12 get extracted as well', nil) - subject.snippets.should == [{ project: nil, id: '12' }] + expect(subject.snippets).to eq([{ project: nil, id: '12' }]) end it 'extracts commit shas' do subject.analyze('commit shas 98cf0ae3 are pulled out as Strings', nil) - subject.commits.should == [{ project: nil, id: '98cf0ae3' }] + expect(subject.commits).to eq([{ project: nil, id: '98cf0ae3' }]) + end + + it 'extracts commit ranges' do + subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4', nil) + expect(subject.commit_ranges).to eq([{ project: nil, id: '98cf0ae3...98cf0ae4' }]) end it 'extracts multiple references and preserves their order' do subject.analyze('@me and @you both care about this', nil) - subject.users.should == [ + expect(subject.users).to eq([ { project: nil, id: 'me' }, { project: nil, id: 'you' } - ] + ]) end it 'leaves the original note unmodified' do text = 'issue #123 is just the worst, @user' subject.analyze(text, nil) - text.should == 'issue #123 is just the worst, @user' + expect(text).to eq('issue #123 is just the worst, @user') end it 'handles all possible kinds of references' do accessors = Gitlab::Markdown::TYPES.map { |t| "#{t}s".to_sym } - subject.should respond_to(*accessors) + expect(subject).to respond_to(*accessors) end context 'with a project' do @@ -62,7 +67,7 @@ describe Gitlab::ReferenceExtractor do project.team << [@u_bar, :guest] subject.analyze('@foo, @baduser, @bar, and @offteam', project) - subject.users_for(project).should == [@u_foo, @u_bar] + expect(subject.users_for(project)).to eq([@u_foo, @u_bar]) end it 'accesses valid issue objects' do @@ -70,7 +75,7 @@ describe Gitlab::ReferenceExtractor do @i1 = create(:issue, project: project) subject.analyze("##{@i0.iid}, ##{@i1.iid}, and #999.", project) - subject.issues_for(project).should == [@i0, @i1] + expect(subject.issues_for(project)).to eq([@i0, @i1]) end it 'accesses valid merge requests' do @@ -78,7 +83,7 @@ describe Gitlab::ReferenceExtractor do @m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'bbb') subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.", project) - subject.merge_requests_for(project).should == [@m1, @m0] + expect(subject.merge_requests_for(project)).to eq([@m1, @m0]) end it 'accesses valid snippets' do @@ -87,7 +92,7 @@ describe Gitlab::ReferenceExtractor do @s2 = create(:project_snippet) subject.analyze("$#{@s0.id}, $999, $#{@s2.id}, $#{@s1.id}", project) - subject.snippets_for(project).should == [@s0, @s1] + expect(subject.snippets_for(project)).to eq([@s0, @s1]) end it 'accesses valid commits' do @@ -96,9 +101,23 @@ describe Gitlab::ReferenceExtractor do subject.analyze("this references commits #{commit.sha[0..6]} and 012345", project) extracted = subject.commits_for(project) - extracted.should have(1).item - extracted[0].sha.should == commit.sha - extracted[0].message.should == commit.message + expect(extracted.size).to eq(1) + expect(extracted[0].sha).to eq(commit.sha) + expect(extracted[0].message).to eq(commit.message) + end + + it 'accesses valid commit ranges' do + commit = project.repository.commit('master') + earlier_commit = project.repository.commit('master~2') + + subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}", + project) + extracted = subject.commit_ranges_for(project) + expect(extracted.size).to eq(1) + expect(extracted[0][0].sha).to eq(earlier_commit.sha) + expect(extracted[0][0].message).to eq(earlier_commit.message) + expect(extracted[0][1].sha).to eq(commit.sha) + expect(extracted[0][1].message).to eq(commit.message) end end end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index a3aae7771bd..1db9f15b790 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -2,20 +2,20 @@ require 'spec_helper' describe Gitlab::Regex do describe 'path regex' do - it { 'gitlab-ce'.should match(Gitlab::Regex.path_regex) } - it { 'gitlab_git'.should match(Gitlab::Regex.path_regex) } - it { '_underscore.js'.should match(Gitlab::Regex.path_regex) } - it { '100px.com'.should match(Gitlab::Regex.path_regex) } - it { '?gitlab'.should_not match(Gitlab::Regex.path_regex) } - it { 'git lab'.should_not match(Gitlab::Regex.path_regex) } - it { 'gitlab.git'.should_not match(Gitlab::Regex.path_regex) } + it { expect('gitlab-ce').to match(Gitlab::Regex.path_regex) } + it { expect('gitlab_git').to match(Gitlab::Regex.path_regex) } + it { expect('_underscore.js').to match(Gitlab::Regex.path_regex) } + it { expect('100px.com').to match(Gitlab::Regex.path_regex) } + it { expect('?gitlab').not_to match(Gitlab::Regex.path_regex) } + it { expect('git lab').not_to match(Gitlab::Regex.path_regex) } + it { expect('gitlab.git').not_to match(Gitlab::Regex.path_regex) } end describe 'project name regex' do - it { 'gitlab-ce'.should match(Gitlab::Regex.project_name_regex) } - it { 'GitLab CE'.should match(Gitlab::Regex.project_name_regex) } - it { '100 lines'.should match(Gitlab::Regex.project_name_regex) } - it { 'gitlab.git'.should match(Gitlab::Regex.project_name_regex) } - it { '?gitlab'.should_not match(Gitlab::Regex.project_name_regex) } + it { expect('gitlab-ce').to match(Gitlab::Regex.project_name_regex) } + it { expect('GitLab CE').to match(Gitlab::Regex.project_name_regex) } + it { expect('100 lines').to match(Gitlab::Regex.project_name_regex) } + it { expect('gitlab.git').to match(Gitlab::Regex.project_name_regex) } + it { expect('?gitlab').not_to match(Gitlab::Regex.project_name_regex) } end end diff --git a/spec/lib/gitlab/satellite/action_spec.rb b/spec/lib/gitlab/satellite/action_spec.rb index 3eb1258d67e..28e3d64ee2b 100644 --- a/spec/lib/gitlab/satellite/action_spec.rb +++ b/spec/lib/gitlab/satellite/action_spec.rb @@ -6,7 +6,7 @@ describe 'Gitlab::Satellite::Action' do describe '#prepare_satellite!' do it 'should be able to fetch timeout from conf' do - Gitlab::Satellite::Action::DEFAULT_OPTIONS[:git_timeout].should == 30.seconds + expect(Gitlab::Satellite::Action::DEFAULT_OPTIONS[:git_timeout]).to eq(30.seconds) end it 'create a repository with a parking branch and one remote: origin' do @@ -15,22 +15,22 @@ describe 'Gitlab::Satellite::Action' do #now lets dirty it up starting_remote_count = repo.git.list_remotes.size - starting_remote_count.should >= 1 + expect(starting_remote_count).to be >= 1 #kind of hookey way to add a second remote origin_uri = repo.git.remote({v: true}).split(" ")[1] begin repo.git.remote({raise: true}, 'add', 'another-remote', origin_uri) repo.git.branch({raise: true}, 'a-new-branch') - repo.heads.size.should > (starting_remote_count) - repo.git.remote().split(" ").size.should > (starting_remote_count) + expect(repo.heads.size).to be > (starting_remote_count) + expect(repo.git.remote().split(" ").size).to be > (starting_remote_count) rescue end repo.git.config({}, "user.name", "#{user.name} -- foo") repo.git.config({}, "user.email", "#{user.email} -- foo") - repo.config['user.name'].should =="#{user.name} -- foo" - repo.config['user.email'].should =="#{user.email} -- foo" + expect(repo.config['user.name']).to eq("#{user.name} -- foo") + expect(repo.config['user.email']).to eq("#{user.email} -- foo") #These must happen in the context of the satellite directory... @@ -42,13 +42,13 @@ describe 'Gitlab::Satellite::Action' do #verify it's clean heads = repo.heads.map(&:name) - heads.size.should == 1 - heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH).should == true + expect(heads.size).to eq(1) + expect(heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH)).to eq(true) remotes = repo.git.remote().split(' ') - remotes.size.should == 1 - remotes.include?('origin').should == true - repo.config['user.name'].should ==user.name - repo.config['user.email'].should ==user.email + expect(remotes.size).to eq(1) + expect(remotes.include?('origin')).to eq(true) + expect(repo.config['user.name']).to eq(user.name) + expect(repo.config['user.email']).to eq(user.email) end end @@ -61,16 +61,16 @@ describe 'Gitlab::Satellite::Action' do #set assumptions FileUtils.rm_f(project.satellite.lock_file) - File.exists?(project.satellite.lock_file).should be_false + expect(File.exists?(project.satellite.lock_file)).to be_falsey satellite_action = Gitlab::Satellite::Action.new(user, project) satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| - repo.should == sat_repo - (File.exists? project.satellite.lock_file).should be_true + expect(repo).to eq(sat_repo) + expect(File.exists? project.satellite.lock_file).to be_truthy called = true end - called.should be_true + expect(called).to be_truthy end @@ -80,19 +80,19 @@ describe 'Gitlab::Satellite::Action' do # Set base assumptions if File.exists? project.satellite.lock_file - FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false + expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey end satellite_action = Gitlab::Satellite::Action.new(user, project) satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| called = true - repo.should == sat_repo - (File.exists? project.satellite.lock_file).should be_true - FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_true + expect(repo).to eq(sat_repo) + expect(File.exists? project.satellite.lock_file).to be_truthy + expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_truthy end - called.should be_true - FileLockStatusChecker.new(project.satellite.lock_file).flocked?.should be_false + expect(called).to be_truthy + expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey end diff --git a/spec/lib/gitlab/satellite/merge_action_spec.rb b/spec/lib/gitlab/satellite/merge_action_spec.rb index 479a73a1081..915e3ff0e51 100644 --- a/spec/lib/gitlab/satellite/merge_action_spec.rb +++ b/spec/lib/gitlab/satellite/merge_action_spec.rb @@ -13,9 +13,9 @@ describe 'Gitlab::Satellite::MergeAction' do describe '#commits_between' do def verify_commits(commits, first_commit_sha, last_commit_sha) - commits.each { |commit| commit.class.should == Gitlab::Git::Commit } - commits.first.id.should == first_commit_sha - commits.last.id.should == last_commit_sha + commits.each { |commit| expect(commit.class).to eq(Gitlab::Git::Commit) } + expect(commits.first.id).to eq(first_commit_sha) + expect(commits.last.id).to eq(last_commit_sha) end context 'on fork' do @@ -35,7 +35,7 @@ describe 'Gitlab::Satellite::MergeAction' do describe '#format_patch' do def verify_content(patch) sample_compare.commits.each do |commit| - patch.include?(commit).should be_true + expect(patch.include?(commit)).to be_truthy end end @@ -57,11 +57,11 @@ describe 'Gitlab::Satellite::MergeAction' do describe '#diffs_between_satellite tested against diff_in_satellite' do def is_a_matching_diff(diff, diffs) diff_count = diff.scan('diff --git').size - diff_count.should >= 1 - diffs.size.should == diff_count + expect(diff_count).to be >= 1 + expect(diffs.size).to eq(diff_count) diffs.each do |a_diff| - a_diff.class.should == Gitlab::Git::Diff - (diff.include? a_diff.diff).should be_true + expect(a_diff.class).to eq(Gitlab::Git::Diff) + expect(diff.include? a_diff.diff).to be_truthy end end @@ -82,23 +82,23 @@ describe 'Gitlab::Satellite::MergeAction' do describe '#can_be_merged?' do context 'on fork' do - it { Gitlab::Satellite::MergeAction.new( + it { expect(Gitlab::Satellite::MergeAction.new( merge_request_fork.author, - merge_request_fork).can_be_merged?.should be_true } + merge_request_fork).can_be_merged?).to be_truthy } - it { Gitlab::Satellite::MergeAction.new( + it { expect(Gitlab::Satellite::MergeAction.new( merge_request_fork_with_conflict.author, - merge_request_fork_with_conflict).can_be_merged?.should be_false } + merge_request_fork_with_conflict).can_be_merged?).to be_falsey } end context 'between branches' do - it { Gitlab::Satellite::MergeAction.new( + it { expect(Gitlab::Satellite::MergeAction.new( merge_request.author, - merge_request).can_be_merged?.should be_true } + merge_request).can_be_merged?).to be_truthy } - it { Gitlab::Satellite::MergeAction.new( + it { expect(Gitlab::Satellite::MergeAction.new( merge_request_with_conflict.author, - merge_request_with_conflict).can_be_merged?.should be_false } + merge_request_with_conflict).can_be_merged?).to be_falsey } end end end diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb index 2b254d6b3a6..ce3ea6c260a 100644 --- a/spec/lib/gitlab/upgrader_spec.rb +++ b/spec/lib/gitlab/upgrader_spec.rb @@ -5,20 +5,20 @@ describe Gitlab::Upgrader do let(:current_version) { Gitlab::VERSION } describe 'current_version_raw' do - it { upgrader.current_version_raw.should == current_version } + it { expect(upgrader.current_version_raw).to eq(current_version) } end describe 'latest_version?' do it 'should be true if newest version' do upgrader.stub(latest_version_raw: current_version) - upgrader.latest_version?.should be_true + expect(upgrader.latest_version?).to be_truthy end end describe 'latest_version_raw' do it 'should be latest version for GitLab 5' do upgrader.stub(current_version_raw: "5.3.0") - upgrader.latest_version_raw.should == "v5.4.2" + expect(upgrader.latest_version_raw).to eq("v5.4.2") end end end diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index eb47bee8336..5153ed15af3 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -5,7 +5,73 @@ describe Gitlab::UrlBuilder do it 'returns the issue url' do issue = create(:issue) url = Gitlab::UrlBuilder.new(:issue).build(issue.id) - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.to_param}/issues/#{issue.iid}" + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" + end + end + + describe 'When asking for an merge request' do + it 'returns the merge request url' do + merge_request = create(:merge_request) + url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id) + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" + end + end + + describe 'When asking for a note on commit' do + let(:note) { create(:note_on_commit) } + let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + + it 'returns the note url' do + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end + + describe 'When asking for a note on commit diff' do + let(:note) { create(:note_on_commit_diff) } + let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + + it 'returns the note url' do + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end + + describe 'When asking for a note on issue' do + let(:issue) { create(:issue) } + let(:note) { create(:note_on_issue, noteable_id: issue.id) } + let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + + it 'returns the note url' do + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" + end + end + + describe 'When asking for a note on merge request' do + let(:merge_request) { create(:merge_request) } + let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) } + let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + + it 'returns the note url' do + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + describe 'When asking for a note on merge request diff' do + let(:merge_request) { create(:merge_request) } + let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) } + let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + + it 'returns the note url' do + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + describe 'When asking for a note on project snippet' do + let(:snippet) { create(:project_snippet) } + let(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) } + let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + + it 'returns the note url' do + expect(url).to eq "#{Settings.gitlab['url']}/#{snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" end end end diff --git a/spec/lib/gitlab/version_info_spec.rb b/spec/lib/gitlab/version_info_spec.rb index 94dccf7a4e5..5afeb1c1ec3 100644 --- a/spec/lib/gitlab/version_info_spec.rb +++ b/spec/lib/gitlab/version_info_spec.rb @@ -12,58 +12,58 @@ describe 'Gitlab::VersionInfo', no_db: true do end context '>' do - it { @v2_0_0.should > @v1_1_0 } - it { @v1_1_0.should > @v1_0_1 } - it { @v1_0_1.should > @v1_0_0 } - it { @v1_0_0.should > @v0_1_0 } - it { @v0_1_0.should > @v0_0_1 } + it { expect(@v2_0_0).to be > @v1_1_0 } + it { expect(@v1_1_0).to be > @v1_0_1 } + it { expect(@v1_0_1).to be > @v1_0_0 } + it { expect(@v1_0_0).to be > @v0_1_0 } + it { expect(@v0_1_0).to be > @v0_0_1 } end context '>=' do - it { @v2_0_0.should >= Gitlab::VersionInfo.new(2, 0, 0) } - it { @v2_0_0.should >= @v1_1_0 } + it { expect(@v2_0_0).to be >= Gitlab::VersionInfo.new(2, 0, 0) } + it { expect(@v2_0_0).to be >= @v1_1_0 } end context '<' do - it { @v0_0_1.should < @v0_1_0 } - it { @v0_1_0.should < @v1_0_0 } - it { @v1_0_0.should < @v1_0_1 } - it { @v1_0_1.should < @v1_1_0 } - it { @v1_1_0.should < @v2_0_0 } + it { expect(@v0_0_1).to be < @v0_1_0 } + it { expect(@v0_1_0).to be < @v1_0_0 } + it { expect(@v1_0_0).to be < @v1_0_1 } + it { expect(@v1_0_1).to be < @v1_1_0 } + it { expect(@v1_1_0).to be < @v2_0_0 } end context '<=' do - it { @v0_0_1.should <= Gitlab::VersionInfo.new(0, 0, 1) } - it { @v0_0_1.should <= @v0_1_0 } + it { expect(@v0_0_1).to be <= Gitlab::VersionInfo.new(0, 0, 1) } + it { expect(@v0_0_1).to be <= @v0_1_0 } end context '==' do - it { @v0_0_1.should == Gitlab::VersionInfo.new(0, 0, 1) } - it { @v0_1_0.should == Gitlab::VersionInfo.new(0, 1, 0) } - it { @v1_0_0.should == Gitlab::VersionInfo.new(1, 0, 0) } + it { expect(@v0_0_1).to eq(Gitlab::VersionInfo.new(0, 0, 1)) } + it { expect(@v0_1_0).to eq(Gitlab::VersionInfo.new(0, 1, 0)) } + it { expect(@v1_0_0).to eq(Gitlab::VersionInfo.new(1, 0, 0)) } end context '!=' do - it { @v0_0_1.should_not == @v0_1_0 } + it { expect(@v0_0_1).not_to eq(@v0_1_0) } end context 'unknown' do - it { @unknown.should_not be @v0_0_1 } - it { @unknown.should_not be Gitlab::VersionInfo.new } + it { expect(@unknown).not_to be @v0_0_1 } + it { expect(@unknown).not_to be Gitlab::VersionInfo.new } it { expect{@unknown > @v0_0_1}.to raise_error(ArgumentError) } it { expect{@unknown < @v0_0_1}.to raise_error(ArgumentError) } end context 'parse' do - it { Gitlab::VersionInfo.parse("1.0.0").should == @v1_0_0 } - it { Gitlab::VersionInfo.parse("1.0.0.1").should == @v1_0_0 } - it { Gitlab::VersionInfo.parse("git 1.0.0b1").should == @v1_0_0 } - it { Gitlab::VersionInfo.parse("git 1.0b1").should_not be_valid } + it { expect(Gitlab::VersionInfo.parse("1.0.0")).to eq(@v1_0_0) } + it { expect(Gitlab::VersionInfo.parse("1.0.0.1")).to eq(@v1_0_0) } + it { expect(Gitlab::VersionInfo.parse("git 1.0.0b1")).to eq(@v1_0_0) } + it { expect(Gitlab::VersionInfo.parse("git 1.0b1")).not_to be_valid } end context 'to_s' do - it { @v1_0_0.to_s.should == "1.0.0" } - it { @unknown.to_s.should == "Unknown" } + it { expect(@v1_0_0.to_s).to eq("1.0.0") } + it { expect(@unknown.to_s).to eq("Unknown") } end end diff --git a/spec/lib/votes_spec.rb b/spec/lib/votes_spec.rb index a88a10d927f..df243a26008 100644 --- a/spec/lib/votes_spec.rb +++ b/spec/lib/votes_spec.rb @@ -5,107 +5,107 @@ describe Issue, 'Votes' do describe "#upvotes" do it "with no notes has a 0/0 score" do - issue.upvotes.should == 0 + expect(issue.upvotes).to eq(0) end it "should recognize non-+1 notes" do add_note "No +1 here" - issue.should have(1).note - issue.notes.first.upvote?.should be_false - issue.upvotes.should == 0 + expect(issue.notes.size).to eq(1) + expect(issue.notes.first.upvote?).to be_falsey + expect(issue.upvotes).to eq(0) end it "should recognize a single +1 note" do add_note "+1 This is awesome" - issue.upvotes.should == 1 + expect(issue.upvotes).to eq(1) end it 'should recognize multiple +1 notes' do add_note '+1 This is awesome', create(:user) add_note '+1 I want this', create(:user) - issue.upvotes.should == 2 + expect(issue.upvotes).to eq(2) end it 'should not count 2 +1 votes from the same user' do add_note '+1 This is awesome' add_note '+1 I want this' - issue.upvotes.should == 1 + expect(issue.upvotes).to eq(1) end end describe "#downvotes" do it "with no notes has a 0/0 score" do - issue.downvotes.should == 0 + expect(issue.downvotes).to eq(0) end it "should recognize non--1 notes" do add_note "Almost got a -1" - issue.should have(1).note - issue.notes.first.downvote?.should be_false - issue.downvotes.should == 0 + expect(issue.notes.size).to eq(1) + expect(issue.notes.first.downvote?).to be_falsey + expect(issue.downvotes).to eq(0) end it "should recognize a single -1 note" do add_note "-1 This is bad" - issue.downvotes.should == 1 + expect(issue.downvotes).to eq(1) end it "should recognize multiple -1 notes" do add_note('-1 This is bad', create(:user)) add_note('-1 Away with this', create(:user)) - issue.downvotes.should == 2 + expect(issue.downvotes).to eq(2) end end describe "#votes_count" do it "with no notes has a 0/0 score" do - issue.votes_count.should == 0 + expect(issue.votes_count).to eq(0) end it "should recognize non notes" do add_note "No +1 here" - issue.should have(1).note - issue.votes_count.should == 0 + expect(issue.notes.size).to eq(1) + expect(issue.votes_count).to eq(0) end it "should recognize a single +1 note" do add_note "+1 This is awesome" - issue.votes_count.should == 1 + expect(issue.votes_count).to eq(1) end it "should recognize a single -1 note" do add_note "-1 This is bad" - issue.votes_count.should == 1 + expect(issue.votes_count).to eq(1) end it "should recognize multiple notes" do add_note('+1 This is awesome', create(:user)) add_note('-1 This is bad', create(:user)) add_note('+1 I want this', create(:user)) - issue.votes_count.should == 3 + expect(issue.votes_count).to eq(3) end it 'should not count 2 -1 votes from the same user' do add_note '-1 This is suspicious' add_note '-1 This is bad' - issue.votes_count.should == 1 + expect(issue.votes_count).to eq(1) end end describe "#upvotes_in_percent" do it "with no notes has a 0% score" do - issue.upvotes_in_percent.should == 0 + expect(issue.upvotes_in_percent).to eq(0) end it "should count a single 1 note as 100%" do add_note "+1 This is awesome" - issue.upvotes_in_percent.should == 100 + expect(issue.upvotes_in_percent).to eq(100) end it 'should count multiple +1 notes as 100%' do add_note('+1 This is awesome', create(:user)) add_note('+1 I want this', create(:user)) - issue.upvotes_in_percent.should == 100 + expect(issue.upvotes_in_percent).to eq(100) end it 'should count fractions for multiple +1 and -1 notes correctly' do @@ -113,24 +113,24 @@ describe Issue, 'Votes' do add_note('+1 I want this', create(:user)) add_note('-1 This is bad', create(:user)) add_note('+1 me too', create(:user)) - issue.upvotes_in_percent.should == 75 + expect(issue.upvotes_in_percent).to eq(75) end end describe "#downvotes_in_percent" do it "with no notes has a 0% score" do - issue.downvotes_in_percent.should == 0 + expect(issue.downvotes_in_percent).to eq(0) end it "should count a single -1 note as 100%" do add_note "-1 This is bad" - issue.downvotes_in_percent.should == 100 + expect(issue.downvotes_in_percent).to eq(100) end it 'should count multiple -1 notes as 100%' do add_note('-1 This is bad', create(:user)) add_note('-1 Away with this', create(:user)) - issue.downvotes_in_percent.should == 100 + expect(issue.downvotes_in_percent).to eq(100) end it 'should count fractions for multiple +1 and -1 notes correctly' do @@ -138,7 +138,7 @@ describe Issue, 'Votes' do add_note('+1 I want this', create(:user)) add_note('-1 This is bad', create(:user)) add_note('+1 me too', create(:user)) - issue.downvotes_in_percent.should == 25 + expect(issue.downvotes_in_percent).to eq(25) end end @@ -151,8 +151,8 @@ describe Issue, 'Votes' do add_note('+1 this looks good now') add_note('+1 This is awesome', create(:user)) add_note('+1 me too', create(:user)) - issue.downvotes.should == 0 - issue.upvotes.should == 5 + expect(issue.downvotes).to eq(0) + expect(issue.upvotes).to eq(5) end it 'should count each users vote only once' do @@ -161,8 +161,8 @@ describe Issue, 'Votes' do add_note '+1 I still like this' add_note '+1 I really like this' add_note '+1 Give me this now!!!!' - issue.downvotes.should == 0 - issue.upvotes.should == 1 + expect(issue.downvotes).to eq(0) + expect(issue.upvotes).to eq(1) end it 'should count a users vote only once without caring about comments' do @@ -171,8 +171,8 @@ describe Issue, 'Votes' do add_note 'Another comment' add_note '+1 vote' add_note 'final comment' - issue.downvotes.should == 0 - issue.upvotes.should == 1 + expect(issue.downvotes).to eq(0) + expect(issue.upvotes).to eq(1) end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index c045f85052c..e3a3b542358 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -5,6 +5,7 @@ describe Notify do include EmailSpec::Matchers include RepoHelpers + let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name } let(:gitlab_sender) { Gitlab.config.gitlab.email_from } let(:recipient) { create(:user, email: 'recipient@example.com') } let(:project) { create(:project) } @@ -16,34 +17,34 @@ describe Notify do shared_examples 'a multiple recipients email' do it 'is sent to the given recipient' do - should deliver_to recipient.notification_email + is_expected.to deliver_to recipient.notification_email end end shared_examples 'an email sent from GitLab' do it 'is sent from GitLab' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq('GitLab') - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(gitlab_sender_display_name) + expect(sender.address).to eq(gitlab_sender) end end shared_examples 'an email starting a new thread' do |message_id_prefix| it 'has a discussion identifier' do - should have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ - should have_header 'X-GitLab-Project', /#{project.name}/ + is_expected.to have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ + is_expected.to have_header 'X-GitLab-Project', /#{project.name}/ end end shared_examples 'an answer to an existing thread' do |thread_id_prefix| it 'has a subject that begins with Re: ' do - should have_subject /^Re: / + is_expected.to have_subject /^Re: / end it 'has headers that reference an existing thread' do - should have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ - should have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ - should have_header 'X-GitLab-Project', /#{project.name}/ + is_expected.to have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ + is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ + is_expected.to have_header 'X-GitLab-Project', /#{project.name}/ end end @@ -58,30 +59,30 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it 'is sent to the new user' do - should deliver_to new_user.email + is_expected.to deliver_to new_user.email end it 'has the correct subject' do - should have_subject /^Account was created for you$/i + is_expected.to have_subject /^Account was created for you$/i end it 'contains the new user\'s login name' do - should have_body_text /#{new_user.email}/ + is_expected.to have_body_text /#{new_user.email}/ end it 'contains the password text' do - should have_body_text /Click here to set your password/ + is_expected.to have_body_text /Click here to set your password/ end it 'includes a link for user to set password' do params = "reset_password_token=#{token}" - should have_body_text( + is_expected.to have_body_text( %r{http://localhost(:\d+)?/users/password/edit\?#{params}} ) end it 'includes a link to the site' do - should have_body_text /#{example_site_path}/ + is_expected.to have_body_text /#{example_site_path}/ end end @@ -95,23 +96,23 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it 'is sent to the new user' do - should deliver_to new_user.email + is_expected.to deliver_to new_user.email end it 'has the correct subject' do - should have_subject /^Account was created for you$/i + is_expected.to have_subject /^Account was created for you$/i end it 'contains the new user\'s login name' do - should have_body_text /#{new_user.email}/ + is_expected.to have_body_text /#{new_user.email}/ end it 'should not contain the new user\'s password' do - should_not have_body_text /password/ + is_expected.not_to have_body_text /password/ end it 'includes a link to the site' do - should have_body_text /#{example_site_path}/ + is_expected.to have_body_text /#{example_site_path}/ end end @@ -123,19 +124,19 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it 'is sent to the new user' do - should deliver_to key.user.email + is_expected.to deliver_to key.user.email end it 'has the correct subject' do - should have_subject /^SSH key was added to your account$/i + is_expected.to have_subject /^SSH key was added to your account$/i end it 'contains the new ssh key title' do - should have_body_text /#{key.title}/ + is_expected.to have_body_text /#{key.title}/ end it 'includes a link to ssh keys page' do - should have_body_text /#{profile_keys_path}/ + is_expected.to have_body_text /#{profile_keys_path}/ end end @@ -145,19 +146,19 @@ describe Notify do subject { Notify.new_email_email(email.id) } it 'is sent to the new user' do - should deliver_to email.user.email + is_expected.to deliver_to email.user.email end it 'has the correct subject' do - should have_subject /^Email was added to your account$/i + is_expected.to have_subject /^Email was added to your account$/i end it 'contains the new email address' do - should have_body_text /#{email.email}/ + is_expected.to have_body_text /#{email.email}/ end it 'includes a link to emails page' do - should have_body_text /#{profile_emails_path}/ + is_expected.to have_body_text /#{profile_emails_path}/ end end @@ -170,18 +171,25 @@ describe Notify do shared_examples 'an assignee email' do it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(current_user.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(current_user.name) + expect(sender.address).to eq(gitlab_sender) end it 'is sent to the assignee' do - should deliver_to assignee.email + is_expected.to deliver_to assignee.email end end context 'for issues' do let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project) } let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: Faker::Lorem.sentence) } + let(:issue_with_image) do + create(:issue, + author: current_user, + assignee: assignee, + project: project, + description: "") + end describe 'that are new' do subject { Notify.new_issue_email(issue.assignee_id, issue.id) } @@ -190,11 +198,11 @@ describe Notify do it_behaves_like 'an email starting a new thread', 'issue' it 'has the correct subject' do - should have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/ + is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/ end it 'contains a link to the new issue' do - should have_body_text /#{project_issue_path project, issue}/ + is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end end @@ -202,7 +210,23 @@ describe Notify do subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) } it 'contains the description' do - should have_body_text /#{issue_with_description.description}/ + is_expected.to have_body_text /#{issue_with_description.description}/ + end + end + + describe 'that contain images' do + let(:png) { File.read("#{Rails.root}/spec/fixtures/dk.png") } + let(:png_encoded) { Base64::encode64(png) } + + before :each do + file_path = File.join(Rails.root, 'public', 'uploads', issue_with_image.project.path_with_namespace, '12345/test.jpg') + allow(File).to receive(:file?).with(file_path).and_return(true) + allow(File).to receive(:read).with(file_path).and_return(png) + end + + subject { Notify.new_issue_email(issue_with_image.assignee_id, issue_with_image.id) } + it 'replaces attached images with inline images' do + is_expected.to have_body_text URI.encode(png_encoded) end end @@ -214,24 +238,24 @@ describe Notify do it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(current_user.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(current_user.name) + expect(sender.address).to eq(gitlab_sender) end it 'has the correct subject' do - should have_subject /#{issue.title} \(##{issue.iid}\)/ + is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/ end it 'contains the name of the previous assignee' do - should have_body_text /#{previous_assignee.name}/ + is_expected.to have_body_text /#{previous_assignee.name}/ end it 'contains the name of the new assignee' do - should have_body_text /#{assignee.name}/ + is_expected.to have_body_text /#{assignee.name}/ end it 'contains a link to the issue' do - should have_body_text /#{project_issue_path project, issue}/ + is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end end @@ -243,24 +267,24 @@ describe Notify do it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(current_user.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(current_user.name) + expect(sender.address).to eq(gitlab_sender) end it 'has the correct subject' do - should have_subject /#{issue.title} \(##{issue.iid}\)/i + is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/i end it 'contains the new status' do - should have_body_text /#{status}/i + is_expected.to have_body_text /#{status}/i end it 'contains the user name' do - should have_body_text /#{current_user.name}/i + is_expected.to have_body_text /#{current_user.name}/i end it 'contains a link to the issue' do - should have_body_text /#{project_issue_path project, issue}/ + is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end end @@ -270,6 +294,14 @@ describe Notify do let(:merge_author) { create(:user) } let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) } let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) } + let(:merge_request_with_image) do + create(:merge_request, + author: current_user, + assignee: assignee, + source_project: project, + target_project: project, + description: "") + end describe 'that are new' do subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } @@ -278,23 +310,23 @@ describe Notify do it_behaves_like 'an email starting a new thread', 'merge_request' it 'has the correct subject' do - should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ end it 'contains a link to the new merge request' do - should have_body_text /#{project_merge_request_path(project, merge_request)}/ + is_expected.to have_body_text /#{namespace_project_merge_request_path(project.namespace, project, merge_request)}/ end it 'contains the source branch for the merge request' do - should have_body_text /#{merge_request.source_branch}/ + is_expected.to have_body_text /#{merge_request.source_branch}/ end it 'contains the target branch for the merge request' do - should have_body_text /#{merge_request.target_branch}/ + is_expected.to have_body_text /#{merge_request.target_branch}/ end it 'has the correct message-id set' do - should have_header 'Message-ID', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>" + is_expected.to have_header 'Message-ID', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>" end end @@ -302,7 +334,23 @@ describe Notify do subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } it 'contains the description' do - should have_body_text /#{merge_request_with_description.description}/ + is_expected.to have_body_text /#{merge_request_with_description.description}/ + end + end + + describe 'that are new and contain contain images in the description' do + let(:png) {File.read("#{Rails.root}/spec/fixtures/dk.png")} + let(:png_encoded) { Base64::encode64(png) } + + before :each do + file_path = File.join(Rails.root, 'public', 'uploads', merge_request_with_image.project.path_with_namespace, '/12345/test.jpg') + allow(File).to receive(:file?).with(file_path).and_return(true) + allow(File).to receive(:read).with(file_path).and_return(png) + end + + subject { Notify.new_merge_request_email(merge_request_with_image.assignee_id, merge_request_with_image.id) } + it 'replaces attached images with inline images' do + is_expected.to have_body_text URI.encode(png_encoded) end end @@ -314,24 +362,24 @@ describe Notify do it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(current_user.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(current_user.name) + expect(sender.address).to eq(gitlab_sender) end it 'has the correct subject' do - should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ end it 'contains the name of the previous assignee' do - should have_body_text /#{previous_assignee.name}/ + is_expected.to have_body_text /#{previous_assignee.name}/ end it 'contains the name of the new assignee' do - should have_body_text /#{assignee.name}/ + is_expected.to have_body_text /#{assignee.name}/ end it 'contains a link to the merge request' do - should have_body_text /#{project_merge_request_path project, merge_request}/ + is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end end @@ -343,24 +391,24 @@ describe Notify do it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(current_user.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(current_user.name) + expect(sender.address).to eq(gitlab_sender) end it 'has the correct subject' do - should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/i + is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/i end it 'contains the new status' do - should have_body_text /#{status}/i + is_expected.to have_body_text /#{status}/i end it 'contains the user name' do - should have_body_text /#{current_user.name}/i + is_expected.to have_body_text /#{current_user.name}/i end it 'contains a link to the merge request' do - should have_body_text /#{project_merge_request_path project, merge_request}/ + is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end end @@ -372,20 +420,20 @@ describe Notify do it 'is sent as the merge author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(merge_author.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(merge_author.name) + expect(sender.address).to eq(gitlab_sender) end it 'has the correct subject' do - should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ end it 'contains the new status' do - should have_body_text /merged/i + is_expected.to have_body_text /merged/i end it 'contains a link to the merge request' do - should have_body_text /#{project_merge_request_path project, merge_request}/ + is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end end end @@ -399,36 +447,39 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it 'has the correct subject' do - should have_subject /Project was moved/ + is_expected.to have_subject /Project was moved/ end it 'contains name of project' do - should have_body_text /#{project.name_with_namespace}/ + is_expected.to have_body_text /#{project.name_with_namespace}/ end it 'contains new user role' do - should have_body_text /#{project.ssh_url_to_repo}/ + is_expected.to have_body_text /#{project.ssh_url_to_repo}/ end end describe 'project access changed' do let(:project) { create(:project) } let(:user) { create(:user) } - let(:project_member) { create(:project_member, - project: project, - user: user) } + let(:project_member) do + create(:project_member, + project: project, + user: user) + end + subject { Notify.project_access_granted_email(project_member.id) } it_behaves_like 'an email sent from GitLab' it 'has the correct subject' do - should have_subject /Access to project was granted/ + is_expected.to have_subject /Access to project was granted/ end it 'contains name of project' do - should have_body_text /#{project.name}/ + is_expected.to have_body_text /#{project.name}/ end it 'contains new user role' do - should have_body_text /#{project_member.human_access}/ + is_expected.to have_body_text /#{project_member.human_access}/ end end @@ -437,29 +488,55 @@ describe Notify do let(:note) { create(:note, project: project, author: note_author) } before :each do - Note.stub(:find).with(note.id).and_return(note) + allow(Note).to receive(:find).with(note.id).and_return(note) end shared_examples 'a note email' do it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(note_author.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(note_author.name) + expect(sender.address).to eq(gitlab_sender) end it 'is sent to the given recipient' do - should deliver_to recipient.notification_email + is_expected.to deliver_to recipient.notification_email end it 'contains the message from the note' do - should have_body_text /#{note.note}/ + is_expected.to have_body_text /#{note.note}/ + end + end + + describe 'on a commit that contains an image' do + let(:commit) { project.repository.commit } + let(:note_with_image) do + create(:note, + project: project, + author: note_author, + note: "") + end + + let(:png) {File.read("#{Rails.root}/spec/fixtures/dk.png")} + let(:png_encoded) { Base64::encode64(png) } + + before :each do + file_path = File.join(Rails.root, 'public', 'uploads', note_with_image.project.path_with_namespace, '12345/test.jpg') + allow(File).to receive(:file?).with(file_path).and_return(true) + allow(File).to receive(:read).with(file_path).and_return(png) + allow(Note).to receive(:find).with(note_with_image.id).and_return(note_with_image) + allow(note_with_image).to receive(:noteable).and_return(commit) + end + + subject { Notify.note_commit_email(recipient.id, note_with_image.id) } + it 'replaces attached images with inline images' do + is_expected.to have_body_text URI.encode(png_encoded) end end describe 'on a commit' do let(:commit) { project.repository.commit } - before(:each) { note.stub(:noteable).and_return(commit) } + before(:each) { allow(note).to receive(:noteable).and_return(commit) } subject { Notify.note_commit_email(recipient.id, note.id) } @@ -467,18 +544,18 @@ describe Notify do it_behaves_like 'an answer to an existing thread', 'commits' it 'has the correct subject' do - should have_subject /#{commit.title} \(#{commit.short_id}\)/ + is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ end it 'contains a link to the commit' do - should have_body_text commit.short_id + is_expected.to have_body_text commit.short_id end end describe 'on a merge request' do let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let(:note_on_merge_request_path) { project_merge_request_path(project, merge_request, anchor: "note_#{note.id}") } - before(:each) { note.stub(:noteable).and_return(merge_request) } + let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") } + before(:each) { allow(note).to receive(:noteable).and_return(merge_request) } subject { Notify.note_merge_request_email(recipient.id, note.id) } @@ -486,18 +563,18 @@ describe Notify do it_behaves_like 'an answer to an existing thread', 'merge_request' it 'has the correct subject' do - should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ end it 'contains a link to the merge request note' do - should have_body_text /#{note_on_merge_request_path}/ + is_expected.to have_body_text /#{note_on_merge_request_path}/ end end describe 'on an issue' do let(:issue) { create(:issue, project: project) } - let(:note_on_issue_path) { project_issue_path(project, issue, anchor: "note_#{note.id}") } - before(:each) { note.stub(:noteable).and_return(issue) } + let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") } + before(:each) { allow(note).to receive(:noteable).and_return(issue) } subject { Notify.note_issue_email(recipient.id, note.id) } @@ -505,11 +582,11 @@ describe Notify do it_behaves_like 'an answer to an existing thread', 'issue' it 'has the correct subject' do - should have_subject /#{issue.title} \(##{issue.iid}\)/ + is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/ end it 'contains a link to the issue note' do - should have_body_text /#{note_on_issue_path}/ + is_expected.to have_body_text /#{note_on_issue_path}/ end end end @@ -525,15 +602,15 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it 'has the correct subject' do - should have_subject /Access to group was granted/ + is_expected.to have_subject /Access to group was granted/ end it 'contains name of project' do - should have_body_text /#{group.name}/ + is_expected.to have_body_text /#{group.name}/ end it 'contains new user role' do - should have_body_text /#{membership.human_access}/ + is_expected.to have_body_text /#{membership.human_access}/ end end @@ -551,15 +628,15 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it 'is sent to the new user' do - should deliver_to 'new-email@mail.com' + is_expected.to deliver_to 'new-email@mail.com' end it 'has the correct subject' do - should have_subject "Confirmation instructions" + is_expected.to have_subject "Confirmation instructions" end it 'includes a link to the site' do - should have_body_text /#{example_site_path}/ + is_expected.to have_body_text /#{example_site_path}/ end end @@ -568,34 +645,87 @@ describe Notify do let(:user) { create(:user) } let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_image_commit.id, sample_commit.id) } let(:commits) { Commit.decorate(compare.commits) } - let(:diff_path) { project_compare_path(project, from: commits.first, to: commits.last) } + let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base), to: Commit.new(compare.head)) } + let(:send_from_committer_email) { false } - subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) } + subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare, false, send_from_committer_email) } it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(user.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(user.name) + expect(sender.address).to eq(gitlab_sender) end it 'is sent to recipient' do - should deliver_to 'devs@company.name' + is_expected.to deliver_to 'devs@company.name' end it 'has the correct subject' do - should have_subject /#{commits.length} new commits pushed to repository/ + is_expected.to have_subject /\[#{project.path_with_namespace}\]\[master\] #{commits.length} commits:/ end it 'includes commits list' do - should have_body_text /Change some files/ + is_expected.to have_body_text /Change some files/ end it 'includes diffs' do - should have_body_text /def archive_formats_regex/ + is_expected.to have_body_text /def archive_formats_regex/ end it 'contains a link to the diff' do - should have_body_text /#{diff_path}/ + is_expected.to have_body_text /#{diff_path}/ + end + + it 'doesn not contain the misleading footer' do + is_expected.not_to have_body_text /you are a member of/ + end + + context "when set to send from committer email if domain matches" do + + let(:send_from_committer_email) { true } + + before do + allow(Gitlab.config.gitlab).to receive(:host).and_return("gitlab.corp.company.com") + end + + context "when the committer email domain is within the GitLab domain" do + + before do + user.update_attribute(:email, "user@company.com") + user.confirm! + end + + it "is sent from the committer email" do + sender = subject.header[:from].addrs[0] + expect(sender.address).to eq(user.email) + end + end + + context "when the committer email domain is not completely within the GitLab domain" do + + before do + user.update_attribute(:email, "user@something.company.com") + user.confirm! + end + + it "is sent from the default email" do + sender = subject.header[:from].addrs[0] + expect(sender.address).to eq(gitlab_sender) + end + end + + context "when the committer email domain is outside the GitLab domain" do + + before do + user.update_attribute(:email, "user@mpany.com") + user.confirm! + end + + it "is sent from the default email" do + sender = subject.header[:from].addrs[0] + expect(sender.address).to eq(gitlab_sender) + end + end end end @@ -604,34 +734,34 @@ describe Notify do let(:user) { create(:user) } let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_commit.parent_id, sample_commit.id) } let(:commits) { Commit.decorate(compare.commits) } - let(:diff_path) { project_commit_path(project, commits.first) } + let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) } subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) } it 'is sent as the author' do sender = subject.header[:from].addrs[0] - sender.display_name.should eq(user.name) - sender.address.should eq(gitlab_sender) + expect(sender.display_name).to eq(user.name) + expect(sender.address).to eq(gitlab_sender) end it 'is sent to recipient' do - should deliver_to 'devs@company.name' + is_expected.to deliver_to 'devs@company.name' end it 'has the correct subject' do - should have_subject /#{commits.first.title}/ + is_expected.to have_subject /#{commits.first.title}/ end it 'includes commits list' do - should have_body_text /Change some files/ + is_expected.to have_body_text /Change some files/ end it 'includes diffs' do - should have_body_text /def archive_formats_regex/ + is_expected.to have_body_text /def archive_formats_regex/ end it 'contains a link to the diff' do - should have_body_text /#{diff_path}/ + is_expected.to have_body_text /#{diff_path}/ end end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index cd6d03e6c1a..d1027f64d13 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -2,20 +2,21 @@ # # Table name: application_settings # -# id :integer not null, primary key -# default_projects_limit :integer -# default_branch_protection :integer -# signup_enabled :boolean -# signin_enabled :boolean -# gravatar_enabled :boolean -# sign_in_text :text -# created_at :datetime -# updated_at :datetime -# home_page_url :string(255) +# id :integer not null, primary key +# default_projects_limit :integer +# signup_enabled :boolean +# signin_enabled :boolean +# gravatar_enabled :boolean +# sign_in_text :text +# created_at :datetime +# updated_at :datetime +# home_page_url :string(255) +# default_branch_protection :integer default(2) +# twitter_sharing_enabled :boolean default(TRUE) # require 'spec_helper' describe ApplicationSetting, models: true do - it { ApplicationSetting.create_from_defaults.should be_valid } + it { expect(ApplicationSetting.create_from_defaults).to be_valid } end diff --git a/spec/models/asana_service_spec.rb b/spec/models/asana_service_spec.rb index 6bebb76f8c7..13c8d54a2af 100644 --- a/spec/models/asana_service_spec.rb +++ b/spec/models/asana_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe AsanaService, models: true do describe 'Associations' do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe 'Validations' do @@ -26,7 +31,7 @@ describe AsanaService, models: true do subject.active = true end - it { should validate_presence_of :api_key } + it { is_expected.to validate_presence_of :api_key } end end @@ -46,13 +51,13 @@ describe AsanaService, models: true do end it 'should call Asana service to created a story' do - Asana::Task.should_receive(:find).with('123456').once + expect(Asana::Task).to receive(:find).with('123456').once @asana.check_commit('related to #123456', 'pushed') end it 'should call Asana service to created a story and close a task' do - Asana::Task.should_receive(:find).with('456789').twice + expect(Asana::Task).to receive(:find).with('456789').twice @asana.check_commit('fix #456789', 'pushed') end diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index 0f31c407c90..8ab72151a69 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -18,22 +18,22 @@ require 'spec_helper' describe BroadcastMessage do subject { create(:broadcast_message) } - it { should be_valid } + it { is_expected.to be_valid } describe :current do it "should return last message if time match" do broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow) - BroadcastMessage.current.should == broadcast_message + expect(BroadcastMessage.current).to eq(broadcast_message) end it "should return nil if time not come" do broadcast_message = create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days) - BroadcastMessage.current.should be_nil + expect(BroadcastMessage.current).to be_nil end it "should return nil if time has passed" do broadcast_message = create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday) - BroadcastMessage.current.should be_nil + expect(BroadcastMessage.current).to be_nil end end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 7a2a7a4ce9b..8b3d88640da 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -6,22 +6,22 @@ describe Commit do describe '#title' do it "returns no_commit_message when safe_message is blank" do - commit.stub(:safe_message).and_return('') - commit.title.should == "--no commit message" + allow(commit).to receive(:safe_message).and_return('') + expect(commit.title).to eq("--no commit message") end it "truncates a message without a newline at 80 characters" do message = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit. Vivamus egestas lacinia lacus, sed rutrum mauris.' - commit.stub(:safe_message).and_return(message) - commit.title.should == "#{message[0..79]}…" + allow(commit).to receive(:safe_message).and_return(message) + expect(commit.title).to eq("#{message[0..79]}…") end it "truncates a message with a newline before 80 characters at the newline" do message = commit.safe_message.split(" ").first - commit.stub(:safe_message).and_return(message + "\n" + message) - commit.title.should == message + allow(commit).to receive(:safe_message).and_return(message + "\n" + message) + expect(commit.title).to eq(message) end it "does not truncates a message with a newline after 80 but less 100 characters" do @@ -30,25 +30,25 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis Vivamus egestas lacinia lacus, sed rutrum mauris. eos - commit.stub(:safe_message).and_return(message) - commit.title.should == message.split("\n").first + allow(commit).to receive(:safe_message).and_return(message) + expect(commit.title).to eq(message.split("\n").first) end end describe "delegation" do subject { commit } - it { should respond_to(:message) } - it { should respond_to(:authored_date) } - it { should respond_to(:committed_date) } - it { should respond_to(:committer_email) } - it { should respond_to(:author_email) } - it { should respond_to(:parents) } - it { should respond_to(:date) } - it { should respond_to(:diffs) } - it { should respond_to(:tree) } - it { should respond_to(:id) } - it { should respond_to(:to_patch) } + it { is_expected.to respond_to(:message) } + it { is_expected.to respond_to(:authored_date) } + it { is_expected.to respond_to(:committed_date) } + it { is_expected.to respond_to(:committer_email) } + it { is_expected.to respond_to(:author_email) } + it { is_expected.to respond_to(:parents) } + it { is_expected.to respond_to(:date) } + it { is_expected.to respond_to(:diffs) } + it { is_expected.to respond_to(:tree) } + it { is_expected.to respond_to(:id) } + it { is_expected.to respond_to(:to_patch) } end describe '#closes_issues' do @@ -58,13 +58,13 @@ eos it 'detects issues that this commit is marked as closing' do commit.stub(safe_message: "Fixes ##{issue.iid}") - commit.closes_issues(project).should == [issue] + expect(commit.closes_issues(project)).to eq([issue]) end it 'does not detect issues from other projects' do ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}" commit.stub(safe_message: "Fixes #{ext_ref}") - commit.closes_issues(project).should be_empty + expect(commit.closes_issues(project)).to be_empty end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 9cbc8990676..557c71b4d2c 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -4,63 +4,63 @@ describe Issue, "Issuable" do let(:issue) { create(:issue) } describe "Associations" do - it { should belong_to(:project) } - it { should belong_to(:author) } - it { should belong_to(:assignee) } - it { should have_many(:notes).dependent(:destroy) } + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:author) } + it { is_expected.to belong_to(:assignee) } + it { is_expected.to have_many(:notes).dependent(:destroy) } end describe "Validation" do before { subject.stub(set_iid: false) } - it { should validate_presence_of(:project) } - it { should validate_presence_of(:iid) } - it { should validate_presence_of(:author) } - it { should validate_presence_of(:title) } - it { should ensure_length_of(:title).is_at_least(0).is_at_most(255) } + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:iid) } + it { is_expected.to validate_presence_of(:author) } + it { is_expected.to validate_presence_of(:title) } + it { is_expected.to ensure_length_of(:title).is_at_least(0).is_at_most(255) } end describe "Scope" do - it { described_class.should respond_to(:opened) } - it { described_class.should respond_to(:closed) } - it { described_class.should respond_to(:assigned) } + it { expect(described_class).to respond_to(:opened) } + it { expect(described_class).to respond_to(:closed) } + it { expect(described_class).to respond_to(:assigned) } end describe ".search" do let!(:searchable_issue) { create(:issue, title: "Searchable issue") } it "matches by title" do - described_class.search('able').should == [searchable_issue] + expect(described_class.search('able')).to eq([searchable_issue]) end end describe "#today?" do it "returns true when created today" do # Avoid timezone differences and just return exactly what we want - Date.stub(:today).and_return(issue.created_at.to_date) - issue.today?.should be_true + allow(Date).to receive(:today).and_return(issue.created_at.to_date) + expect(issue.today?).to be_truthy end it "returns false when not created today" do - Date.stub(:today).and_return(Date.yesterday) - issue.today?.should be_false + allow(Date).to receive(:today).and_return(Date.yesterday) + expect(issue.today?).to be_falsey end end describe "#new?" do it "returns true when created today and record hasn't been updated" do - issue.stub(:today?).and_return(true) - issue.new?.should be_true + allow(issue).to receive(:today?).and_return(true) + expect(issue.new?).to be_truthy end it "returns false when not created today" do - issue.stub(:today?).and_return(false) - issue.new?.should be_false + allow(issue).to receive(:today?).and_return(false) + expect(issue.new?).to be_falsey end it "returns false when record has been updated" do - issue.stub(:today?).and_return(true) + allow(issue).to receive(:today?).and_return(true) issue.touch - issue.new?.should be_false + expect(issue.new?).to be_falsey end end end diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index ca6f11b2a4d..eadb941a3fa 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -8,7 +8,7 @@ describe Issue, "Mentionable" do subject { issue.mentioned_users } - it { should include(user) } - it { should_not include(user2) } + it { is_expected.to include(user) } + it { is_expected.not_to include(user2) } end end diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index adbbbac875f..b32be8d7a7c 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -19,7 +19,7 @@ describe DeployKey do let(:deploy_key) { create(:deploy_key, projects: [project]) } describe "Associations" do - it { should have_many(:deploy_keys_projects) } - it { should have_many(:projects) } + it { is_expected.to have_many(:deploy_keys_projects) } + it { is_expected.to have_many(:projects) } end end diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index 3e0e25ee39a..aacd9bf38bf 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -13,12 +13,12 @@ require 'spec_helper' describe DeployKeysProject do describe "Associations" do - it { should belong_to(:deploy_key) } - it { should belong_to(:project) } + it { is_expected.to belong_to(:deploy_key) } + it { is_expected.to belong_to(:project) } end describe "Validation" do - it { should validate_presence_of(:project_id) } - it { should validate_presence_of(:deploy_key_id) } + it { is_expected.to validate_presence_of(:project_id) } + it { is_expected.to validate_presence_of(:deploy_key_id) } end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 204ae9da704..0f32f162a10 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -18,16 +18,16 @@ require 'spec_helper' describe Event do describe "Associations" do - it { should belong_to(:project) } - it { should belong_to(:target) } + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:target) } end describe "Respond to" do - it { should respond_to(:author_name) } - it { should respond_to(:author_email) } - it { should respond_to(:issue_title) } - it { should respond_to(:merge_request_title) } - it { should respond_to(:commits) } + it { is_expected.to respond_to(:author_name) } + it { is_expected.to respond_to(:author_email) } + it { is_expected.to respond_to(:issue_title) } + it { is_expected.to respond_to(:merge_request_title) } + it { is_expected.to respond_to(:commits) } end describe "Push event" do @@ -58,10 +58,10 @@ describe Event do ) end - it { @event.push?.should be_true } - it { @event.proper?.should be_true } - it { @event.tag?.should be_false } - it { @event.branch_name.should == "master" } - it { @event.author.should == @user } + it { expect(@event.push?).to be_truthy } + it { expect(@event.proper?).to be_truthy } + it { expect(@event.tag?).to be_falsey } + it { expect(@event.branch_name).to eq("master") } + it { expect(@event.author).to eq(@user) } end end diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb index 1845c6103f5..7d0ad44a92c 100644 --- a/spec/models/forked_project_link_spec.rb +++ b/spec/models/forked_project_link_spec.rb @@ -21,11 +21,11 @@ describe ForkedProjectLink, "add link on fork" do end it "project_to should know it is forked" do - @project_to.forked?.should be_true + expect(@project_to.forked?).to be_truthy end it "project should know who it is forked from" do - @project_to.forked_from_project.should == project_from + expect(@project_to.forked_from_project).to eq(project_from) end end @@ -43,15 +43,15 @@ describe :forked_from_project do it "project_to should know it is forked" do - project_to.forked?.should be_true + expect(project_to.forked?).to be_truthy end it "project_from should not be forked" do - project_from.forked?.should be_false + expect(project_from.forked?).to be_falsey end it "project_to.destroy should destroy fork_link" do - forked_project_link.should_receive(:destroy) + expect(forked_project_link).to receive(:destroy) project_to.destroy end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 1d4ba8a2b85..9428224a64f 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -19,29 +19,29 @@ describe Group do let!(:group) { create(:group) } describe "Associations" do - it { should have_many :projects } - it { should have_many :group_members } + it { is_expected.to have_many :projects } + it { is_expected.to have_many :group_members } end - it { should validate_presence_of :name } - it { should validate_uniqueness_of(:name) } - it { should validate_presence_of :path } - it { should validate_uniqueness_of(:path) } - it { should_not validate_presence_of :owner } + it { is_expected.to validate_presence_of :name } + it { is_expected.to validate_uniqueness_of(:name) } + it { is_expected.to validate_presence_of :path } + it { is_expected.to validate_uniqueness_of(:path) } + it { is_expected.not_to validate_presence_of :owner } describe :users do - it { group.users.should == group.owners } + it { expect(group.users).to eq(group.owners) } end describe :human_name do - it { group.human_name.should == group.name } + it { expect(group.human_name).to eq(group.name) } end describe :add_users do let(:user) { create(:user) } before { group.add_user(user, GroupMember::MASTER) } - it { group.group_members.masters.map(&:user).should include(user) } + it { expect(group.group_members.masters.map(&:user)).to include(user) } end describe :add_users do @@ -49,10 +49,10 @@ describe Group do before { group.add_users([user.id], GroupMember::GUEST) } it "should update the group permission" do - group.group_members.guests.map(&:user).should include(user) + expect(group.group_members.guests.map(&:user)).to include(user) group.add_users([user.id], GroupMember::DEVELOPER) - group.group_members.developers.map(&:user).should include(user) - group.group_members.guests.map(&:user).should_not include(user) + expect(group.group_members.developers.map(&:user)).to include(user) + expect(group.group_members.guests.map(&:user)).not_to include(user) end end @@ -62,12 +62,12 @@ describe Group do it "should be true if avatar is image" do group.update_attribute(:avatar, 'uploads/avatar.png') - group.avatar_type.should be_true + expect(group.avatar_type).to be_truthy end it "should be false if avatar is html page" do group.update_attribute(:avatar, 'uploads/avatar.html') - group.avatar_type.should == ["only images allowed"] + expect(group.avatar_type).to eq(["only images allowed"]) end end end diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb index 6ec82438dfe..96bf74d45da 100644 --- a/spec/models/hooks/service_hook_spec.rb +++ b/spec/models/hooks/service_hook_spec.rb @@ -19,6 +19,6 @@ require "spec_helper" describe ServiceHook do describe "Associations" do - it { should belong_to :service } + it { is_expected.to belong_to :service } end end diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb index 8deb732de9c..810b311a40b 100644 --- a/spec/models/hooks/system_hook_spec.rb +++ b/spec/models/hooks/system_hook_spec.rb @@ -26,32 +26,32 @@ describe SystemHook do it "project_create hook" do Projects::CreateService.new(create(:user), name: 'empty').execute - WebMock.should have_requested(:post, @system_hook.url).with(body: /project_create/).once + expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /project_create/).once end it "project_destroy hook" do user = create(:user) project = create(:empty_project, namespace: user.namespace) Projects::DestroyService.new(project, user, {}).execute - WebMock.should have_requested(:post, @system_hook.url).with(body: /project_destroy/).once + expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /project_destroy/).once end it "user_create hook" do create(:user) - WebMock.should have_requested(:post, @system_hook.url).with(body: /user_create/).once + expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_create/).once end it "user_destroy hook" do user = create(:user) user.destroy - WebMock.should have_requested(:post, @system_hook.url).with(body: /user_destroy/).once + expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_destroy/).once end it "project_create hook" do user = create(:user) project = create(:project) project.team << [user, :master] - WebMock.should have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once + expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once end it "project_destroy hook" do @@ -59,12 +59,12 @@ describe SystemHook do project = create(:project) project.team << [user, :master] project.project_members.destroy_all - WebMock.should have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once + expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once end it 'group create hook' do create(:group) - WebMock.should have_requested(:post, @system_hook.url).with( + expect(WebMock).to have_requested(:post, @system_hook.url).with( body: /group_create/ ).once end @@ -72,7 +72,7 @@ describe SystemHook do it 'group destroy hook' do group = create(:group) group.destroy - WebMock.should have_requested(:post, @system_hook.url).with( + expect(WebMock).to have_requested(:post, @system_hook.url).with( body: /group_destroy/ ).once end @@ -81,7 +81,7 @@ describe SystemHook do group = create(:group) user = create(:user) group.add_user(user, Gitlab::Access::MASTER) - WebMock.should have_requested(:post, @system_hook.url).with( + expect(WebMock).to have_requested(:post, @system_hook.url).with( body: /user_add_to_group/ ).once end @@ -91,7 +91,7 @@ describe SystemHook do user = create(:user) group.add_user(user, Gitlab::Access::MASTER) group.group_members.destroy_all - WebMock.should have_requested(:post, @system_hook.url).with( + expect(WebMock).to have_requested(:post, @system_hook.url).with( body: /user_remove_from_group/ ).once end diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index e9c04ee89cb..67ec9193ad7 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -19,25 +19,25 @@ require 'spec_helper' describe ProjectHook do describe "Associations" do - it { should belong_to :project } + it { is_expected.to belong_to :project } end describe "Mass assignment" do end describe "Validations" do - it { should validate_presence_of(:url) } + it { is_expected.to validate_presence_of(:url) } context "url format" do - it { should allow_value("http://example.com").for(:url) } - it { should allow_value("https://excample.com").for(:url) } - it { should allow_value("http://test.com/api").for(:url) } - it { should allow_value("http://test.com/api?key=abc").for(:url) } - it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) } + it { is_expected.to allow_value("http://example.com").for(:url) } + it { is_expected.to allow_value("https://excample.com").for(:url) } + it { is_expected.to allow_value("http://test.com/api").for(:url) } + it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) } + it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) } - it { should_not allow_value("example.com").for(:url) } - it { should_not allow_value("ftp://example.com").for(:url) } - it { should_not allow_value("herp-and-derp").for(:url) } + it { is_expected.not_to allow_value("example.com").for(:url) } + it { is_expected.not_to allow_value("ftp://example.com").for(:url) } + it { is_expected.not_to allow_value("herp-and-derp").for(:url) } end end @@ -53,22 +53,22 @@ describe ProjectHook do it "POSTs to the web hook URL" do @project_hook.execute(@data) - WebMock.should have_requested(:post, @project_hook.url).once + expect(WebMock).to have_requested(:post, @project_hook.url).once end it "POSTs the data as JSON" do json = @data.to_json @project_hook.execute(@data) - WebMock.should have_requested(:post, @project_hook.url).with(body: json).once + expect(WebMock).to have_requested(:post, @project_hook.url).with(body: json).once end it "catches exceptions" do - WebHook.should_receive(:post).and_raise("Some HTTP Post error") + expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") - lambda { + expect { @project_hook.execute(@data) - }.should raise_error + }.to raise_error end end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 6b6efe832e5..087e40c3d84 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -21,14 +21,14 @@ require 'spec_helper' describe Issue do describe "Associations" do - it { should belong_to(:milestone) } + it { is_expected.to belong_to(:milestone) } end describe "Mass assignment" do end describe 'modules' do - it { should include_module(Issuable) } + it { is_expected.to include_module(Issuable) } end subject { create(:issue) } @@ -36,10 +36,10 @@ describe Issue do describe '#is_being_reassigned?' do it 'returns true if the issue assignee has changed' do subject.assignee = create(:user) - subject.is_being_reassigned?.should be_true + expect(subject.is_being_reassigned?).to be_truthy end it 'returns false if the issue assignee has not changed' do - subject.is_being_reassigned?.should be_false + expect(subject.is_being_reassigned?).to be_falsey end end @@ -51,7 +51,7 @@ describe Issue do issue = create :issue, assignee: user end - Issue.open_for(user).count.should eq 2 + expect(Issue.open_for(user).count).to eq 2 end end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 95c0aed0ffe..a212b95a7d6 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -16,67 +16,67 @@ require 'spec_helper' describe Key do describe "Associations" do - it { should belong_to(:user) } + it { is_expected.to belong_to(:user) } end describe "Mass assignment" do end describe "Validation" do - it { should validate_presence_of(:title) } - it { should validate_presence_of(:key) } - it { should ensure_length_of(:title).is_within(0..255) } - it { should ensure_length_of(:key).is_within(0..5000) } + it { is_expected.to validate_presence_of(:title) } + it { is_expected.to validate_presence_of(:key) } + it { is_expected.to ensure_length_of(:title).is_within(0..255) } + it { is_expected.to ensure_length_of(:key).is_within(0..5000) } end describe "Methods" do - it { should respond_to :projects } + it { is_expected.to respond_to :projects } end context "validation of uniqueness" do let(:user) { create(:user) } it "accepts the key once" do - build(:key, user: user).should be_valid + expect(build(:key, user: user)).to be_valid end it "does not accept the exact same key twice" do create(:key, user: user) - build(:key, user: user).should_not be_valid + expect(build(:key, user: user)).not_to be_valid end it "does not accept a duplicate key with a different comment" do create(:key, user: user) duplicate = build(:key, user: user) duplicate.key << ' extra comment' - duplicate.should_not be_valid + expect(duplicate).not_to be_valid end end context "validate it is a fingerprintable key" do it "accepts the fingerprintable key" do - build(:key).should be_valid + expect(build(:key)).to be_valid end it "rejects the unfingerprintable key (contains space in middle)" do - build(:key_with_a_space_in_the_middle).should_not be_valid + expect(build(:key_with_a_space_in_the_middle)).not_to be_valid end it "rejects the unfingerprintable key (not a key)" do - build(:invalid_key).should_not be_valid + expect(build(:invalid_key)).not_to be_valid end end context 'callbacks' do it 'should add new key to authorized_file' do @key = build(:personal_key, id: 7) - GitlabShellWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key) + expect(GitlabShellWorker).to receive(:perform_async).with(:add_key, @key.shell_id, @key.key) @key.save end it 'should remove key from authorized_file' do @key = create(:personal_key) - GitlabShellWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key) + expect(GitlabShellWorker).to receive(:perform_async).with(:remove_key, @key.shell_id, @key.key) @key.destroy end end diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb index 0db60432ad3..8c240826582 100644 --- a/spec/models/label_link_spec.rb +++ b/spec/models/label_link_spec.rb @@ -14,8 +14,8 @@ require 'spec_helper' describe LabelLink do let(:label) { create(:label_link) } - it { label.should be_valid } + it { expect(label).to be_valid } - it { should belong_to(:label) } - it { should belong_to(:target) } + it { is_expected.to belong_to(:label) } + it { is_expected.to belong_to(:target) } end diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 31634648f04..8644ac46605 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -14,30 +14,30 @@ require 'spec_helper' describe Label do let(:label) { create(:label) } - it { label.should be_valid } + it { expect(label).to be_valid } - it { should belong_to(:project) } + it { is_expected.to belong_to(:project) } describe 'Validation' do it 'should validate color code' do - build(:label, color: 'G-ITLAB').should_not be_valid - build(:label, color: 'AABBCC').should_not be_valid - build(:label, color: '#AABBCCEE').should_not be_valid - build(:label, color: '#GGHHII').should_not be_valid - build(:label, color: '#').should_not be_valid - build(:label, color: '').should_not be_valid + expect(build(:label, color: 'G-ITLAB')).not_to be_valid + expect(build(:label, color: 'AABBCC')).not_to be_valid + expect(build(:label, color: '#AABBCCEE')).not_to be_valid + expect(build(:label, color: '#GGHHII')).not_to be_valid + expect(build(:label, color: '#')).not_to be_valid + expect(build(:label, color: '')).not_to be_valid - build(:label, color: '#AABBCC').should be_valid + expect(build(:label, color: '#AABBCC')).to be_valid end it 'should validate title' do - build(:label, title: 'G,ITLAB').should_not be_valid - build(:label, title: 'G?ITLAB').should_not be_valid - build(:label, title: 'G&ITLAB').should_not be_valid - build(:label, title: '').should_not be_valid + expect(build(:label, title: 'G,ITLAB')).not_to be_valid + expect(build(:label, title: 'G?ITLAB')).not_to be_valid + expect(build(:label, title: 'G&ITLAB')).not_to be_valid + expect(build(:label, title: '')).not_to be_valid - build(:label, title: 'GITLAB').should be_valid - build(:label, title: 'gitlab').should be_valid + expect(build(:label, title: 'GITLAB')).to be_valid + expect(build(:label, title: 'gitlab')).to be_valid end end end diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb index 38657de6793..e04f1741b24 100644 --- a/spec/models/members/group_member_spec.rb +++ b/spec/models/members/group_member_spec.rb @@ -21,7 +21,7 @@ describe GroupMember do it "should send email to user" do membership = build(:group_member) membership.stub(notification_service: double('NotificationService').as_null_object) - membership.should_receive(:notification_service) + expect(membership).to receive(:notification_service) membership.save end end @@ -33,12 +33,12 @@ describe GroupMember do end it "should send email to user" do - @membership.should_receive(:notification_service) + expect(@membership).to receive(:notification_service) @membership.update_attribute(:access_level, GroupMember::MASTER) end it "does not send an email when the access level has not changed" do - @membership.should_not_receive(:notification_service) + expect(@membership).not_to receive(:notification_service) @membership.update_attribute(:access_level, GroupMember::OWNER) end end diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index 9b5f89b6d7d..521721f3577 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -33,19 +33,19 @@ describe ProjectMember do @status = @project_2.team.import(@project_1) end - it { @status.should be_true } + it { expect(@status).to be_truthy } describe 'project 2 should get user 1 as developer. user_2 should not be changed' do - it { @project_2.users.should include(@user_1) } - it { @project_2.users.should include(@user_2) } + it { expect(@project_2.users).to include(@user_1) } + it { expect(@project_2.users).to include(@user_2) } - it { @abilities.allowed?(@user_1, :write_project, @project_2).should be_true } - it { @abilities.allowed?(@user_2, :read_project, @project_2).should be_true } + it { expect(@abilities.allowed?(@user_1, :write_project, @project_2)).to be_truthy } + it { expect(@abilities.allowed?(@user_2, :read_project, @project_2)).to be_truthy } end describe 'project 1 should not be changed' do - it { @project_1.users.should include(@user_1) } - it { @project_1.users.should_not include(@user_2) } + it { expect(@project_1.users).to include(@user_1) } + it { expect(@project_1.users).not_to include(@user_2) } end end @@ -64,12 +64,12 @@ describe ProjectMember do ) end - it { @project_1.users.should include(@user_1) } - it { @project_1.users.should include(@user_2) } + it { expect(@project_1.users).to include(@user_1) } + it { expect(@project_1.users).to include(@user_2) } - it { @project_2.users.should include(@user_1) } - it { @project_2.users.should include(@user_2) } + it { expect(@project_2.users).to include(@user_1) } + it { expect(@project_2.users).to include(@user_2) } end describe :truncate_teams do @@ -86,7 +86,7 @@ describe ProjectMember do ProjectMember.truncate_teams([@project_1.id, @project_2.id]) end - it { @project_1.users.should be_empty } - it { @project_2.users.should be_empty } + it { expect(@project_1.users).to be_empty } + it { expect(@project_2.users).to be_empty } end end diff --git a/spec/models/members_spec.rb b/spec/models/members_spec.rb index cea653ec285..dfd3f7feb6b 100644 --- a/spec/models/members_spec.rb +++ b/spec/models/members_spec.rb @@ -2,19 +2,19 @@ require 'spec_helper' describe Member do describe "Associations" do - it { should belong_to(:user) } + it { is_expected.to belong_to(:user) } end describe "Validation" do subject { Member.new(access_level: Member::GUEST) } - it { should validate_presence_of(:user) } - it { should validate_presence_of(:source) } - it { should validate_inclusion_of(:access_level).in_array(Gitlab::Access.values) } + it { is_expected.to validate_presence_of(:user) } + it { is_expected.to validate_presence_of(:source) } + it { is_expected.to validate_inclusion_of(:access_level).in_array(Gitlab::Access.values) } end describe "Delegate methods" do - it { should respond_to(:user_name) } - it { should respond_to(:user_email) } + it { is_expected.to respond_to(:user_name) } + it { is_expected.to respond_to(:user_email) } end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 9585cf09768..d40503d791c 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -25,35 +25,35 @@ require 'spec_helper' describe MergeRequest do describe "Validation" do - it { should validate_presence_of(:target_branch) } - it { should validate_presence_of(:source_branch) } + it { is_expected.to validate_presence_of(:target_branch) } + it { is_expected.to validate_presence_of(:source_branch) } end describe "Mass assignment" do end describe "Respond to" do - it { should respond_to(:unchecked?) } - it { should respond_to(:can_be_merged?) } - it { should respond_to(:cannot_be_merged?) } + it { is_expected.to respond_to(:unchecked?) } + it { is_expected.to respond_to(:can_be_merged?) } + it { is_expected.to respond_to(:cannot_be_merged?) } end describe 'modules' do - it { should include_module(Issuable) } + it { is_expected.to include_module(Issuable) } end describe "#mr_and_commit_notes" do let!(:merge_request) { create(:merge_request) } before do - merge_request.stub(:commits) { [merge_request.source_project.repository.commit] } + allow(merge_request).to receive(:commits) { [merge_request.source_project.repository.commit] } create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.project) create(:note, noteable: merge_request, project: merge_request.project) end it "should include notes for commits" do - merge_request.commits.should_not be_empty - merge_request.mr_and_commit_notes.count.should == 2 + expect(merge_request.commits).not_to be_empty + expect(merge_request.mr_and_commit_notes.count).to eq(2) end end @@ -62,10 +62,10 @@ describe MergeRequest do describe '#is_being_reassigned?' do it 'returns true if the merge_request assignee has changed' do subject.assignee = create(:user) - subject.is_being_reassigned?.should be_true + expect(subject.is_being_reassigned?).to be_truthy end it 'returns false if the merge request assignee has not changed' do - subject.is_being_reassigned?.should be_false + expect(subject.is_being_reassigned?).to be_falsey end end @@ -74,11 +74,11 @@ describe MergeRequest do subject.source_project = create(:project, namespace: create(:group)) subject.target_project = create(:project, namespace: create(:group)) - subject.for_fork?.should be_true + expect(subject.for_fork?).to be_truthy end it 'returns false if is not for a fork' do - subject.for_fork?.should be_false + expect(subject.for_fork?).to be_falsey end end @@ -96,14 +96,14 @@ describe MergeRequest do it 'accesses the set of issues that will be closed on acceptance' do subject.project.stub(default_branch: subject.target_branch) - subject.closes_issues.should == [issue0, issue1].sort_by(&:id) + expect(subject.closes_issues).to eq([issue0, issue1].sort_by(&:id)) end it 'only lists issues as to be closed if it targets the default branch' do subject.project.stub(default_branch: 'master') subject.target_branch = 'something-else' - subject.closes_issues.should be_empty + expect(subject.closes_issues).to be_empty end it 'detects issues mentioned in the description' do @@ -111,7 +111,7 @@ describe MergeRequest do subject.description = "Closes ##{issue2.iid}" subject.project.stub(default_branch: subject.target_branch) - subject.closes_issues.should include(issue2) + expect(subject.closes_issues).to include(issue2) end end diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index a3071c3251a..45171e1bf64 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -17,8 +17,8 @@ require 'spec_helper' describe Milestone do describe "Associations" do - it { should belong_to(:project) } - it { should have_many(:issues) } + it { is_expected.to belong_to(:project) } + it { is_expected.to have_many(:issues) } end describe "Mass assignment" do @@ -26,8 +26,8 @@ describe Milestone do describe "Validation" do before { subject.stub(set_iid: false) } - it { should validate_presence_of(:title) } - it { should validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:title) } + it { is_expected.to validate_presence_of(:project) } end let(:milestone) { create(:milestone) } @@ -36,30 +36,30 @@ describe Milestone do describe "#percent_complete" do it "should not count open issues" do milestone.issues << issue - milestone.percent_complete.should == 0 + expect(milestone.percent_complete).to eq(0) end it "should count closed issues" do issue.close milestone.issues << issue - milestone.percent_complete.should == 100 + expect(milestone.percent_complete).to eq(100) end it "should recover from dividing by zero" do - milestone.issues.should_receive(:count).and_return(0) - milestone.percent_complete.should == 100 + expect(milestone.issues).to receive(:count).and_return(0) + expect(milestone.percent_complete).to eq(100) end end describe "#expires_at" do it "should be nil when due_date is unset" do milestone.update_attributes(due_date: nil) - milestone.expires_at.should be_nil + expect(milestone.expires_at).to be_nil end it "should not be nil when due_date is set" do milestone.update_attributes(due_date: Date.tomorrow) - milestone.expires_at.should be_present + expect(milestone.expires_at).to be_present end end @@ -69,7 +69,7 @@ describe Milestone do milestone.stub(due_date: Date.today.prev_year) end - it { milestone.expired?.should be_true } + it { expect(milestone.expired?).to be_truthy } end context "not expired" do @@ -77,7 +77,7 @@ describe Milestone do milestone.stub(due_date: Date.today.next_year) end - it { milestone.expired?.should be_false } + it { expect(milestone.expired?).to be_falsey } end end @@ -89,7 +89,7 @@ describe Milestone do ) end - it { milestone.percent_complete.should == 75 } + it { expect(milestone.percent_complete).to eq(75) } end describe :items_count do @@ -99,14 +99,14 @@ describe Milestone do milestone.merge_requests << create(:merge_request) end - it { milestone.closed_items_count.should == 1 } - it { milestone.open_items_count.should == 2 } - it { milestone.total_items_count.should == 3 } - it { milestone.is_empty?.should be_false } + it { expect(milestone.closed_items_count).to eq(1) } + it { expect(milestone.open_items_count).to eq(2) } + it { expect(milestone.total_items_count).to eq(3) } + it { expect(milestone.is_empty?).to be_falsey } end describe :can_be_closed? do - it { milestone.can_be_closed?.should be_true } + it { expect(milestone.can_be_closed?).to be_truthy } end describe :is_empty? do @@ -116,7 +116,7 @@ describe Milestone do end it 'Should return total count of issues and merge requests assigned to milestone' do - milestone.total_items_count.should eq 2 + expect(milestone.total_items_count).to eq 2 end end @@ -129,14 +129,14 @@ describe Milestone do end it 'should be true if milestone active and all nested issues closed' do - milestone.can_be_closed?.should be_true + expect(milestone.can_be_closed?).to be_truthy end it 'should be false if milestone active and not all nested issues closed' do issue.milestone = milestone issue.save - milestone.can_be_closed?.should be_false + expect(milestone.can_be_closed?).to be_falsey end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 3562ebed1ff..ed6845c82cc 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -18,29 +18,29 @@ require 'spec_helper' describe Namespace do let!(:namespace) { create(:namespace) } - it { should have_many :projects } - it { should validate_presence_of :name } - it { should validate_uniqueness_of(:name) } - it { should validate_presence_of :path } - it { should validate_uniqueness_of(:path) } - it { should validate_presence_of :owner } + it { is_expected.to have_many :projects } + it { is_expected.to validate_presence_of :name } + it { is_expected.to validate_uniqueness_of(:name) } + it { is_expected.to validate_presence_of :path } + it { is_expected.to validate_uniqueness_of(:path) } + it { is_expected.to validate_presence_of :owner } describe "Mass assignment" do end describe "Respond to" do - it { should respond_to(:human_name) } - it { should respond_to(:to_param) } + it { is_expected.to respond_to(:human_name) } + it { is_expected.to respond_to(:to_param) } end - it { Namespace.global_id.should == 'GLN' } + it { expect(Namespace.global_id).to eq('GLN') } describe :to_param do - it { namespace.to_param.should == namespace.path } + it { expect(namespace.to_param).to eq(namespace.path) } end describe :human_name do - it { namespace.human_name.should == namespace.owner_name } + it { expect(namespace.human_name).to eq(namespace.owner_name) } end describe :search do @@ -48,8 +48,8 @@ describe Namespace do @namespace = create :namespace end - it { Namespace.search(@namespace.path).should == [@namespace] } - it { Namespace.search('unknown').should == [] } + it { expect(Namespace.search(@namespace.path)).to eq([@namespace]) } + it { expect(Namespace.search('unknown')).to eq([]) } end describe :move_dir do @@ -66,13 +66,23 @@ describe Namespace do new_path = @namespace.path + "_new" @namespace.stub(path_was: @namespace.path) @namespace.stub(path: new_path) - @namespace.move_dir.should be_true + expect(@namespace.move_dir).to be_truthy end end describe :rm_dir do it "should remove dir" do - namespace.rm_dir.should be_true + expect(namespace.rm_dir).to be_truthy end end + + describe :find_by_path_or_name do + before do + @namespace = create(:namespace, name: 'WoW', path: 'woW') + end + + it { expect(Namespace.find_by_path_or_name('wow')).to eq(@namespace) } + it { expect(Namespace.find_by_path_or_name('WOW')).to eq(@namespace) } + it { expect(Namespace.find_by_path_or_name('unknown')).to eq(nil) } + end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 6ab7162c15c..17cb439c90e 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -21,17 +21,17 @@ require 'spec_helper' describe Note do describe "Associations" do - it { should belong_to(:project) } - it { should belong_to(:noteable) } - it { should belong_to(:author).class_name('User') } + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:noteable) } + it { is_expected.to belong_to(:author).class_name('User') } end describe "Mass assignment" do end describe "Validation" do - it { should validate_presence_of(:note) } - it { should validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:note) } + it { is_expected.to validate_presence_of(:project) } end describe "Voting score" do @@ -39,44 +39,44 @@ describe Note do it "recognizes a neutral note" do note = create(:votable_note, note: "This is not a +1 note") - note.should_not be_upvote - note.should_not be_downvote + expect(note).not_to be_upvote + expect(note).not_to be_downvote end it "recognizes a neutral emoji note" do note = build(:votable_note, note: "I would :+1: this, but I don't want to") - note.should_not be_upvote - note.should_not be_downvote + expect(note).not_to be_upvote + expect(note).not_to be_downvote end it "recognizes a +1 note" do note = create(:votable_note, note: "+1 for this") - note.should be_upvote + expect(note).to be_upvote end it "recognizes a +1 emoji as a vote" do note = build(:votable_note, note: ":+1: for this") - note.should be_upvote + expect(note).to be_upvote end it "recognizes a thumbsup emoji as a vote" do note = build(:votable_note, note: ":thumbsup: for this") - note.should be_upvote + expect(note).to be_upvote end it "recognizes a -1 note" do note = create(:votable_note, note: "-1 for this") - note.should be_downvote + expect(note).to be_downvote end it "recognizes a -1 emoji as a vote" do note = build(:votable_note, note: ":-1: for this") - note.should be_downvote + expect(note).to be_downvote end it "recognizes a thumbsdown emoji as a vote" do note = build(:votable_note, note: ":thumbsdown: for this") - note.should be_downvote + expect(note).to be_downvote end end @@ -87,22 +87,22 @@ describe Note do let!(:commit) { note.noteable } it "should be accessible through #noteable" do - note.commit_id.should == commit.id - note.noteable.should be_a(Commit) - note.noteable.should == commit + expect(note.commit_id).to eq(commit.id) + expect(note.noteable).to be_a(Commit) + expect(note.noteable).to eq(commit) end it "should save a valid note" do - note.commit_id.should == commit.id + expect(note.commit_id).to eq(commit.id) note.noteable == commit end it "should be recognized by #for_commit?" do - note.should be_for_commit + expect(note).to be_for_commit end it "should not be votable" do - note.should_not be_votable + expect(note).not_to be_votable end end @@ -111,20 +111,20 @@ describe Note do let!(:commit) { note.noteable } it "should save a valid note" do - note.commit_id.should == commit.id - note.noteable.id.should == commit.id + expect(note.commit_id).to eq(commit.id) + expect(note.noteable.id).to eq(commit.id) end it "should be recognized by #for_diff_line?" do - note.should be_for_diff_line + expect(note).to be_for_diff_line end it "should be recognized by #for_commit_diff_line?" do - note.should be_for_commit_diff_line + expect(note).to be_for_commit_diff_line end it "should not be votable" do - note.should_not be_votable + expect(note).not_to be_votable end end @@ -132,7 +132,7 @@ describe Note do let!(:note) { create(:note_on_issue, note: "+1 from me") } it "should not be votable" do - note.should be_votable + expect(note).to be_votable end end @@ -140,7 +140,7 @@ describe Note do let!(:note) { create(:note_on_merge_request, note: "+1 from me") } it "should be votable" do - note.should be_votable + expect(note).to be_votable end end @@ -148,7 +148,7 @@ describe Note do let!(:note) { create(:note_on_merge_request_diff, note: "+1 from me") } it "should not be votable" do - note.should_not be_votable + expect(note).not_to be_votable end end @@ -161,20 +161,35 @@ describe Note do subject { Note.create_status_change_note(thing, project, author, status, nil) } it 'creates and saves a Note' do - should be_a Note - subject.id.should_not be_nil + is_expected.to be_a Note + expect(subject.id).not_to be_nil end - its(:noteable) { should == thing } - its(:project) { should == thing.project } - its(:author) { should == author } - its(:note) { should =~ /Status changed to #{status}/ } + describe '#noteable' do + subject { super().noteable } + it { is_expected.to eq(thing) } + end + + describe '#project' do + subject { super().project } + it { is_expected.to eq(thing.project) } + end + + describe '#author' do + subject { super().author } + it { is_expected.to eq(author) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to match(/Status changed to #{status}/) } + end it 'appends a back-reference if a closing mentionable is supplied' do commit = double('commit', gfm_reference: 'commit 123456') n = Note.create_status_change_note(thing, project, author, status, commit) - n.note.should =~ /Status changed to #{status} by commit 123456/ + expect(n.note).to match(/Status changed to #{status} by commit 123456/) end end @@ -187,19 +202,41 @@ describe Note do subject { Note.create_assignee_change_note(thing, project, author, assignee) } context 'creates and saves a Note' do - it { should be_a Note } - its(:id) { should_not be_nil } + it { is_expected.to be_a Note } + + describe '#id' do + subject { super().id } + it { is_expected.not_to be_nil } + end + end + + describe '#noteable' do + subject { super().noteable } + it { is_expected.to eq(thing) } end - its(:noteable) { should == thing } - its(:project) { should == thing.project } - its(:author) { should == author } - its(:note) { should =~ /Reassigned to @#{assignee.username}/ } + describe '#project' do + subject { super().project } + it { is_expected.to eq(thing.project) } + end + + describe '#author' do + subject { super().author } + it { is_expected.to eq(author) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to match(/Reassigned to @#{assignee.username}/) } + end context 'assignee is removed' do let(:assignee) { nil } - its(:note) { should =~ /Assignee removed/ } + describe '#note' do + subject { super().note } + it { is_expected.to match(/Assignee removed/) } + end end end @@ -216,64 +253,144 @@ describe Note do context 'issue from a merge request' do subject { Note.create_cross_reference_note(issue, mergereq, author, project) } - it { should be_valid } - its(:noteable) { should == issue } - its(:project) { should == issue.project } - its(:author) { should == author } - its(:note) { should == "_mentioned in merge request !#{mergereq.iid}_" } + it { is_expected.to be_valid } + + describe '#noteable' do + subject { super().noteable } + it { is_expected.to eq(issue) } + end + + describe '#project' do + subject { super().project } + it { is_expected.to eq(issue.project) } + end + + describe '#author' do + subject { super().author } + it { is_expected.to eq(author) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("_mentioned in merge request !#{mergereq.iid}_") } + end end context 'issue from a commit' do subject { Note.create_cross_reference_note(issue, commit, author, project) } - it { should be_valid } - its(:noteable) { should == issue } - its(:note) { should == "_mentioned in commit #{commit.sha}_" } + it { is_expected.to be_valid } + + describe '#noteable' do + subject { super().noteable } + it { is_expected.to eq(issue) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("_mentioned in commit #{commit.sha}_") } + end end context 'merge request from an issue' do subject { Note.create_cross_reference_note(mergereq, issue, author, project) } - it { should be_valid } - its(:noteable) { should == mergereq } - its(:project) { should == mergereq.project } - its(:note) { should == "_mentioned in issue ##{issue.iid}_" } + it { is_expected.to be_valid } + + describe '#noteable' do + subject { super().noteable } + it { is_expected.to eq(mergereq) } + end + + describe '#project' do + subject { super().project } + it { is_expected.to eq(mergereq.project) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("_mentioned in issue ##{issue.iid}_") } + end end context 'commit from a merge request' do subject { Note.create_cross_reference_note(commit, mergereq, author, project) } - it { should be_valid } - its(:noteable) { should == commit } - its(:project) { should == project } - its(:note) { should == "_mentioned in merge request !#{mergereq.iid}_" } + it { is_expected.to be_valid } + + describe '#noteable' do + subject { super().noteable } + it { is_expected.to eq(commit) } + end + + describe '#project' do + subject { super().project } + it { is_expected.to eq(project) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("_mentioned in merge request !#{mergereq.iid}_") } + end end context 'commit contained in a merge request' do subject { Note.create_cross_reference_note(mergereq.commits.first, mergereq, author, project) } - it { should be_nil } + it { is_expected.to be_nil } end context 'commit from issue' do subject { Note.create_cross_reference_note(commit, issue, author, project) } - it { should be_valid } - its(:noteable_type) { should == "Commit" } - its(:noteable_id) { should be_nil } - its(:commit_id) { should == commit.id } - its(:note) { should == "_mentioned in issue ##{issue.iid}_" } + it { is_expected.to be_valid } + + describe '#noteable_type' do + subject { super().noteable_type } + it { is_expected.to eq("Commit") } + end + + describe '#noteable_id' do + subject { super().noteable_id } + it { is_expected.to be_nil } + end + + describe '#commit_id' do + subject { super().commit_id } + it { is_expected.to eq(commit.id) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("_mentioned in issue ##{issue.iid}_") } + end end context 'commit from commit' do let(:parent_commit) { commit.parents.first } subject { Note.create_cross_reference_note(commit, parent_commit, author, project) } - it { should be_valid } - its(:noteable_type) { should == "Commit" } - its(:noteable_id) { should be_nil } - its(:commit_id) { should == commit.id } - its(:note) { should == "_mentioned in commit #{parent_commit.id}_" } + it { is_expected.to be_valid } + + describe '#noteable_type' do + subject { super().noteable_type } + it { is_expected.to eq("Commit") } + end + + describe '#noteable_id' do + subject { super().noteable_id } + it { is_expected.to be_nil } + end + + describe '#commit_id' do + subject { super().commit_id } + it { is_expected.to eq(commit.id) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("_mentioned in commit #{parent_commit.id}_") } + end end end @@ -289,11 +406,11 @@ describe Note do end it 'detects if a mentionable has already been mentioned' do - Note.cross_reference_exists?(issue, commit0).should be_true + expect(Note.cross_reference_exists?(issue, commit0)).to be_truthy end it 'detects if a mentionable has not already been mentioned' do - Note.cross_reference_exists?(issue, commit1).should be_false + expect(Note.cross_reference_exists?(issue, commit1)).to be_falsey end context 'commit on commit' do @@ -301,8 +418,8 @@ describe Note do Note.create_cross_reference_note(commit0, commit1, author, project) end - it { Note.cross_reference_exists?(commit0, commit1).should be_true } - it { Note.cross_reference_exists?(commit1, commit0).should be_false } + it { expect(Note.cross_reference_exists?(commit0, commit1)).to be_truthy } + it { expect(Note.cross_reference_exists?(commit1, commit0)).to be_falsey } end end @@ -315,22 +432,22 @@ describe Note do it 'should recognize user-supplied notes as non-system' do @note = create(:note_on_issue) - @note.should_not be_system + expect(@note).not_to be_system end it 'should identify status-change notes as system notes' do @note = Note.create_status_change_note(issue, project, author, 'closed', nil) - @note.should be_system + expect(@note).to be_system end it 'should identify cross-reference notes as system notes' do @note = Note.create_cross_reference_note(issue, other, author, project) - @note.should be_system + expect(@note).to be_system end it 'should identify assignee-change notes as system notes' do @note = Note.create_assignee_change_note(issue, project, author, assignee) - @note.should be_system + expect(@note).to be_system end end @@ -351,9 +468,9 @@ describe Note do @p2.project_members.create(user: @u3, access_level: ProjectMember::GUEST) end - it { @abilities.allowed?(@u1, :read_note, @p1).should be_false } - it { @abilities.allowed?(@u2, :read_note, @p1).should be_true } - it { @abilities.allowed?(@u3, :read_note, @p1).should be_false } + it { expect(@abilities.allowed?(@u1, :read_note, @p1)).to be_falsey } + it { expect(@abilities.allowed?(@u2, :read_note, @p1)).to be_truthy } + it { expect(@abilities.allowed?(@u3, :read_note, @p1)).to be_falsey } end describe :write do @@ -362,9 +479,9 @@ describe Note do @p2.project_members.create(user: @u3, access_level: ProjectMember::DEVELOPER) end - it { @abilities.allowed?(@u1, :write_note, @p1).should be_false } - it { @abilities.allowed?(@u2, :write_note, @p1).should be_true } - it { @abilities.allowed?(@u3, :write_note, @p1).should be_false } + it { expect(@abilities.allowed?(@u1, :write_note, @p1)).to be_falsey } + it { expect(@abilities.allowed?(@u2, :write_note, @p1)).to be_truthy } + it { expect(@abilities.allowed?(@u3, :write_note, @p1)).to be_falsey } end describe :admin do @@ -374,9 +491,9 @@ describe Note do @p2.project_members.create(user: @u3, access_level: ProjectMember::MASTER) end - it { @abilities.allowed?(@u1, :admin_note, @p1).should be_false } - it { @abilities.allowed?(@u2, :admin_note, @p1).should be_true } - it { @abilities.allowed?(@u3, :admin_note, @p1).should be_false } + it { expect(@abilities.allowed?(@u1, :admin_note, @p1)).to be_falsey } + it { expect(@abilities.allowed?(@u2, :admin_note, @p1)).to be_truthy } + it { expect(@abilities.allowed?(@u3, :admin_note, @p1)).to be_falsey } end end diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb index 5c8d1e7438b..1ee19003543 100644 --- a/spec/models/project_security_spec.rb +++ b/spec/models/project_security_spec.rb @@ -23,7 +23,7 @@ describe Project do describe "Non member rules" do it "should deny for non-project users any actions" do admin_actions.each do |action| - @abilities.allowed?(@u1, action, @p1).should be_false + expect(@abilities.allowed?(@u1, action, @p1)).to be_falsey end end end @@ -35,7 +35,7 @@ describe Project do it "should allow for project user any guest actions" do guest_actions.each do |action| - @abilities.allowed?(@u2, action, @p1).should be_true + expect(@abilities.allowed?(@u2, action, @p1)).to be_truthy end end end @@ -47,7 +47,7 @@ describe Project do it "should allow for project user any report actions" do report_actions.each do |action| - @abilities.allowed?(@u2, action, @p1).should be_true + expect(@abilities.allowed?(@u2, action, @p1)).to be_truthy end end end @@ -60,13 +60,13 @@ describe Project do it "should deny for developer master-specific actions" do [dev_actions - report_actions].each do |action| - @abilities.allowed?(@u2, action, @p1).should be_false + expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey end end it "should allow for project user any dev actions" do dev_actions.each do |action| - @abilities.allowed?(@u3, action, @p1).should be_true + expect(@abilities.allowed?(@u3, action, @p1)).to be_truthy end end end @@ -79,13 +79,13 @@ describe Project do it "should deny for developer master-specific actions" do [master_actions - dev_actions].each do |action| - @abilities.allowed?(@u2, action, @p1).should be_false + expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey end end it "should allow for project user any master actions" do master_actions.each do |action| - @abilities.allowed?(@u3, action, @p1).should be_true + expect(@abilities.allowed?(@u3, action, @p1)).to be_truthy end end end @@ -98,13 +98,13 @@ describe Project do it "should deny for masters admin-specific actions" do [admin_actions - master_actions].each do |action| - @abilities.allowed?(@u2, action, @p1).should be_false + expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey end end it "should allow for project owner any admin actions" do admin_actions.each do |action| - @abilities.allowed?(@u4, action, @p1).should be_true + expect(@abilities.allowed?(@u4, action, @p1)).to be_truthy end end end diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb index 005dd41fea9..91730da1eec 100644 --- a/spec/models/project_services/assembla_service_spec.rb +++ b/spec/models/project_services/assembla_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe AssemblaService, models: true do describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe "Execute" do @@ -40,7 +45,7 @@ describe AssemblaService, models: true do it "should call Assembla API" do @assembla_service.execute(@sample_data) - WebMock.should have_requested(:post, @api_url).with( + expect(WebMock).to have_requested(:post, @api_url).with( body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/ ).once end diff --git a/spec/models/project_services/buildbox_service_spec.rb b/spec/models/project_services/buildbox_service_spec.rb index 1d9ca51be16..39d7df54cf0 100644 --- a/spec/models/project_services/buildbox_service_spec.rb +++ b/spec/models/project_services/buildbox_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe BuildboxService do describe 'Associations' do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe 'commits methods' do @@ -38,35 +43,39 @@ describe BuildboxService do describe :webhook_url do it 'returns the webhook url' do - @service.webhook_url.should == + expect(@service.webhook_url).to eq( 'https://webhook.buildbox.io/deliver/secret-sauce-webhook-token' + ) end end describe :commit_status_path do it 'returns the correct status page' do - @service.commit_status_path('2ab7834c').should == + expect(@service.commit_status_path('2ab7834c')).to eq( 'https://gitlab.buildbox.io/status/secret-sauce-status-token.json?commit=2ab7834c' + ) end end describe :build_page do it 'returns the correct build page' do - @service.build_page('2ab7834c').should == + expect(@service.build_page('2ab7834c')).to eq( 'https://buildbox.io/account-name/example-project/builds?commit=2ab7834c' + ) end end describe :builds_page do it 'returns the correct path to the builds page' do - @service.builds_path.should == + expect(@service.builds_path).to eq( 'https://buildbox.io/account-name/example-project/builds?branch=default-brancho' + ) end end describe :status_img_path do it 'returns the correct path to the status image' do - @service.status_img_path.should == 'https://badge.buildbox.io/secret-sauce-status-token.svg' + expect(@service.status_img_path).to eq('https://badge.buildbox.io/secret-sauce-status-token.svg') end end end diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb index ac156719b43..73f68301a34 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/project_services/flowdock_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe FlowdockService do describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe "Execute" do @@ -39,7 +44,7 @@ describe FlowdockService do it "should call FlowDock API" do @flowdock_service.execute(@sample_data) - WebMock.should have_requested(:post, @api_url).with( + expect(WebMock).to have_requested(:post, @api_url).with( body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/ ).once end diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb index 2c560c11dac..d44064bbe6a 100644 --- a/spec/models/project_services/gemnasium_service_spec.rb +++ b/spec/models/project_services/gemnasium_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe GemnasiumService do describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe "Execute" do @@ -36,7 +41,7 @@ describe GemnasiumService do @sample_data = Gitlab::PushDataBuilder.build_sample(project, user) end it "should call Gemnasium service" do - Gemnasium::GitlabService.should_receive(:execute).with(an_instance_of(Hash)).once + expect(Gemnasium::GitlabService).to receive(:execute).with(an_instance_of(Hash)).once @gemnasium_service.execute(@sample_data) end end diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index 83277058fbb..8bfb19e524b 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe GitlabCiService do describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe "Mass assignment" do @@ -34,11 +39,11 @@ describe GitlabCiService do end describe :commit_status_path do - it { @service.commit_status_path("2ab7834c").should == "http://ci.gitlab.org/projects/2/commits/2ab7834c/status.json?token=verySecret"} + it { expect(@service.commit_status_path("2ab7834c")).to eq("http://ci.gitlab.org/projects/2/commits/2ab7834c/status.json?token=verySecret")} end describe :build_page do - it { @service.build_page("2ab7834c").should == "http://ci.gitlab.org/projects/2/commits/2ab7834c"} + it { expect(@service.build_page("2ab7834c")).to eq("http://ci.gitlab.org/projects/2/commits/2ab7834c")} end end end diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb new file mode 100644 index 00000000000..959044dc727 --- /dev/null +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -0,0 +1,65 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# + +require 'spec_helper' + +describe GitlabIssueTrackerService do + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + + describe 'project and issue urls' do + let(:project) { create(:project) } + + context 'with absolute urls' do + before do + @service = project.create_gitlab_issue_tracker_service(active: true) + end + + after do + @service.destroy! + end + + it 'should give the correct path' do + expect(@service.project_url).to eq("/#{project.path_with_namespace}/issues") + expect(@service.new_issue_url).to eq("/#{project.path_with_namespace}/issues/new") + expect(@service.issue_url(432)).to eq("/#{project.path_with_namespace}/issues/432") + end + end + + context 'with enabled relative urls' do + before do + Settings.gitlab.stub(:relative_url_root).and_return("/gitlab/root") + @service = project.create_gitlab_issue_tracker_service(active: true) + end + + after do + @service.destroy! + end + + it 'should give the correct path' do + expect(@service.project_url).to eq("/gitlab/root/#{project.path_with_namespace}/issues") + expect(@service.new_issue_url).to eq("/gitlab/root/#{project.path_with_namespace}/issues/new") + expect(@service.issue_url(432)).to eq("/gitlab/root/#{project.path_with_namespace}/issues/432") + end + end + end +end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb new file mode 100644 index 00000000000..8ab847e6432 --- /dev/null +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -0,0 +1,217 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# + +require 'spec_helper' + +describe HipchatService do + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe "Execute" do + let(:hipchat) { HipchatService.new } + let(:user) { create(:user, username: 'username') } + let(:project) { create(:project, name: 'project') } + let(:api_url) { 'https://hipchat.example.com/v2/room/123456/notification?auth_token=verySecret' } + let(:project_name) { project.name_with_namespace.gsub(/\s/, '') } + + before(:each) do + hipchat.stub( + project_id: project.id, + project: project, + room: 123456, + server: 'https://hipchat.example.com', + token: 'verySecret' + ) + WebMock.stub_request(:post, api_url) + end + + context 'push events' do + let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } + + it "should call Hipchat API for push events" do + hipchat.execute(push_sample_data) + + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should create a push message" do + message = hipchat.send(:create_push_message, push_sample_data) + + obj_attr = push_sample_data[:object_attributes] + branch = push_sample_data[:ref].gsub('refs/heads/', '') + expect(message).to include("#{user.name} pushed to branch " \ + "<a href=\"#{project.web_url}/commits/#{branch}\">#{branch}</a> of " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>") + end + end + + context 'tag_push events' do + let(:push_sample_data) { Gitlab::PushDataBuilder.build(project, user, Gitlab::Git::BLANK_SHA, '1' * 40, 'refs/tags/test', []) } + + it "should call Hipchat API for tag push events" do + hipchat.execute(push_sample_data) + + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should create a tag push message" do + message = hipchat.send(:create_push_message, push_sample_data) + + obj_attr = push_sample_data[:object_attributes] + expect(message).to eq("#{user.name} pushed new tag " \ + "<a href=\"#{project.web_url}/commits/test\">test</a> to " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>\n") + end + end + + context 'issue events' do + let(:issue) { create(:issue, title: 'Awesome issue', description: 'please fix') } + let(:issue_service) { Issues::CreateService.new(project, user) } + let(:issues_sample_data) { issue_service.hook_data(issue, 'open') } + + it "should call Hipchat API for issue events" do + hipchat.execute(issues_sample_data) + + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should create an issue message" do + message = hipchat.send(:create_issue_message, issues_sample_data) + + obj_attr = issues_sample_data[:object_attributes] + expect(message).to eq("#{user.name} opened " \ + "<a href=\"#{obj_attr[:url]}\">issue ##{obj_attr["iid"]}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>Awesome issue</b>" \ + "<pre>please fix</pre>") + end + end + + context 'merge request events' do + let(:merge_request) { create(:merge_request, description: 'please fix', title: 'Awesome merge request', target_project: project, source_project: project) } + let(:merge_service) { MergeRequests::CreateService.new(project, user) } + let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') } + + it "should call Hipchat API for merge requests events" do + hipchat.execute(merge_sample_data) + + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should create a merge request message" do + message = hipchat.send(:create_merge_request_message, + merge_sample_data) + + obj_attr = merge_sample_data[:object_attributes] + expect(message).to eq("#{user.name} opened " \ + "<a href=\"#{obj_attr[:url]}\">merge request ##{obj_attr["iid"]}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>Awesome merge request</b>" \ + "<pre>please fix</pre>") + end + end + + context "Note events" do + let(:user) { create(:user) } + let(:project) { create(:project, creator_id: user.id) } + let(:issue) { create(:issue, project: project) } + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let(:snippet) { create(:project_snippet, project: project) } + let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } + let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") } + let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")} + let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") } + + it "should call Hipchat API for commit comment events" do + data = Gitlab::NoteDataBuilder.build(commit_note, user) + hipchat.execute(data) + + expect(WebMock).to have_requested(:post, api_url).once + + message = hipchat.send(:create_message, data) + + obj_attr = data[:object_attributes] + commit_id = Commit.truncate_sha(data[:commit][:id]) + title = hipchat.send(:format_title, data[:commit][:message]) + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "#{title}" \ + "<pre>a comment on a commit</pre>") + end + + it "should call Hipchat API for merge request comment events" do + data = Gitlab::NoteDataBuilder.build(merge_request_note, user) + hipchat.execute(data) + + expect(WebMock).to have_requested(:post, api_url).once + + message = hipchat.send(:create_message, data) + + obj_attr = data[:object_attributes] + merge_id = data[:merge_request]['iid'] + title = data[:merge_request]['title'] + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">merge request ##{merge_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>#{title}</b>" \ + "<pre>merge request note</pre>") + end + + it "should call Hipchat API for issue comment events" do + data = Gitlab::NoteDataBuilder.build(issue_note, user) + hipchat.execute(data) + + message = hipchat.send(:create_message, data) + + obj_attr = data[:object_attributes] + issue_id = data[:issue]['iid'] + title = data[:issue]['title'] + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>#{title}</b>" \ + "<pre>issue note</pre>") + end + + it "should call Hipchat API for snippet comment events" do + data = Gitlab::NoteDataBuilder.build(snippet_note, user) + hipchat.execute(data) + + expect(WebMock).to have_requested(:post, api_url).once + + message = hipchat.send(:create_message, data) + + obj_attr = data[:object_attributes] + snippet_id = data[:snippet]['id'] + title = data[:snippet]['title'] + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>#{title}</b>" \ + "<pre>snippet note</pre>") + end + end + end +end diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb new file mode 100644 index 00000000000..d55399bc360 --- /dev/null +++ b/spec/models/project_services/irker_service_spec.rb @@ -0,0 +1,108 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# + +require 'spec_helper' +require 'socket' +require 'json' + +describe IrkerService do + describe 'Associations' do + it { should belong_to :project } + it { should have_one :service_hook } + end + + describe 'Validations' do + before do + subject.active = true + subject.properties['recipients'] = _recipients + end + + context 'active' do + let(:_recipients) { nil } + it { should validate_presence_of :recipients } + end + + context 'too many recipients' do + let(:_recipients) { 'a b c d' } + it 'should add an error if there is too many recipients' do + subject.send :check_recipients_count + subject.errors.should_not be_blank + end + end + + context '3 recipients' do + let(:_recipients) { 'a b c' } + it 'should not add an error if there is 3 recipients' do + subject.send :check_recipients_count + subject.errors.should be_blank + end + end + end + + describe 'Execute' do + let(:irker) { IrkerService.new } + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } + + let(:recipients) { '#commits' } + let(:colorize_messages) { '1' } + + before do + irker.stub( + active: true, + project: project, + project_id: project.id, + service_hook: true, + properties: { + 'recipients' => recipients, + 'colorize_messages' => colorize_messages + } + ) + irker.settings = { + server_ip: 'localhost', + server_port: 6659, + max_channels: 3, + default_irc_uri: 'irc://chat.freenode.net/' + } + irker.valid? + @irker_server = TCPServer.new 'localhost', 6659 + end + + after do + @irker_server.close + end + + it 'should send valid JSON messages to an Irker listener' do + irker.execute(sample_data) + + conn = @irker_server.accept + conn.readlines.each do |line| + msg = JSON.load(line.chomp("\n")) + msg.keys.should match_array(['to', 'privmsg']) + if msg['to'].is_a?(String) + msg['to'].should == 'irc://chat.freenode.net/#commits' + else + msg['to'].should match_array(['irc://chat.freenode.net/#commits']) + end + end + conn.close + end + end +end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 99ca04eff6e..355911e6377 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe JiraService do describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe "Validations" do @@ -26,9 +31,9 @@ describe JiraService do subject.active = true end - it { should validate_presence_of :project_url } - it { should validate_presence_of :issues_url } - it { should validate_presence_of :new_issue_url } + it { is_expected.to validate_presence_of :project_url } + it { is_expected.to validate_presence_of :issues_url } + it { is_expected.to validate_presence_of :new_issue_url } end end @@ -79,7 +84,7 @@ describe JiraService do "new_issue_url" => "http://jira.sample/projects/project_a/issues/new" } } - Gitlab.config.stub(:issues_tracker).and_return(settings) + allow(Gitlab.config).to receive(:issues_tracker).and_return(settings) @service = project.create_jira_service(active: true) end diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index f2813d66c7d..5a18fd09bfc 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe PushoverService do describe 'Associations' do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe 'Validations' do @@ -26,9 +31,9 @@ describe PushoverService do subject.active = true end - it { should validate_presence_of :api_key } - it { should validate_presence_of :user_key } - it { should validate_presence_of :priority } + it { is_expected.to validate_presence_of :api_key } + it { is_expected.to validate_presence_of :user_key } + it { is_expected.to validate_presence_of :priority } end end @@ -63,7 +68,7 @@ describe PushoverService do it 'should call Pushover API' do pushover.execute(sample_data) - WebMock.should have_requested(:post, api_url).once + expect(WebMock).to have_requested(:post, api_url).once end end end diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb new file mode 100644 index 00000000000..8bca1fef44c --- /dev/null +++ b/spec/models/project_services/slack_service/issue_message_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe SlackService::IssueMessage do + subject { SlackService::IssueMessage.new(args) } + + let(:args) { + { + user: { + name: 'Test User', + username: 'Test User' + }, + project_name: 'project_name', + project_url: 'somewhere.com', + + object_attributes: { + title: 'Issue title', + id: 10, + iid: 100, + assignee_id: 1, + url: 'url', + action: 'open', + state: 'opened', + description: 'issue description' + } + } + } + + let(:color) { '#345' } + + context 'open' do + it 'returns a message regarding opening of issues' do + expect(subject.pretext).to eq( + 'Test User opened <url|issue #100> in <somewhere.com|project_name>: '\ + '*Issue title*') + expect(subject.attachments).to eq([ + { + text: "issue description", + color: color, + } + ]) + end + end + + context 'close' do + before do + args[:object_attributes][:action] = 'close' + args[:object_attributes][:state] = 'closed' + end + it 'returns a message regarding closing of issues' do + expect(subject.pretext). to eq( + 'Test User closed <url|issue #100> in <somewhere.com|project_name>: '\ + '*Issue title*') + expect(subject.attachments).to be_empty + end + end +end diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/slack_service/merge_message_spec.rb new file mode 100644 index 00000000000..aeb408aa766 --- /dev/null +++ b/spec/models/project_services/slack_service/merge_message_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe SlackService::MergeMessage do + subject { SlackService::MergeMessage.new(args) } + + let(:args) { + { + user: { + name: 'Test User', + username: 'Test User' + }, + project_name: 'project_name', + project_url: 'somewhere.com', + + object_attributes: { + title: "Issue title\nSecond line", + id: 10, + iid: 100, + assignee_id: 1, + url: 'url', + state: 'opened', + description: 'issue description', + source_branch: 'source_branch', + target_branch: 'target_branch', + } + } + } + + let(:color) { '#345' } + + context 'open' do + it 'returns a message regarding opening of merge requests' do + expect(subject.pretext).to eq( + 'Test User opened <somewhere.com/merge_requests/100|merge request #100> '\ + 'in <somewhere.com|project_name>: *Issue title*') + expect(subject.attachments).to be_empty + end + end + + context 'close' do + before do + args[:object_attributes][:state] = 'closed' + end + it 'returns a message regarding closing of merge requests' do + expect(subject.pretext).to eq( + 'Test User closed <somewhere.com/merge_requests/100|merge request #100> '\ + 'in <somewhere.com|project_name>: *Issue title*') + expect(subject.attachments).to be_empty + end + end +end diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb new file mode 100644 index 00000000000..21fb575480b --- /dev/null +++ b/spec/models/project_services/slack_service/note_message_spec.rb @@ -0,0 +1,129 @@ +require 'spec_helper' + +describe SlackService::NoteMessage do + let(:color) { '#345' } + + before do + @args = { + user: { + name: 'Test User', + username: 'username', + avatar_url: 'http://fakeavatar' + }, + project_name: 'project_name', + project_url: 'somewhere.com', + repository: { + name: 'project_name', + url: 'somewhere.com', + }, + object_attributes: { + id: 10, + note: 'comment on a commit', + url: 'url', + noteable_type: 'Commit' + } + } + end + + context 'commit notes' do + before do + @args[:object_attributes][:note] = 'comment on a commit' + @args[:object_attributes][:noteable_type] = 'Commit' + @args[:commit] = { + id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23', + message: "Added a commit message\ndetails\n123\n" + } + end + + it 'returns a message regarding notes on commits' do + message = SlackService::NoteMessage.new(@args) + expect(message.pretext).to eq("Test User commented on " \ + "<url|commit 5f163b2b> in <somewhere.com|project_name>: " \ + "*Added a commit message*") + expected_attachments = [ + { + text: "comment on a commit", + color: color, + } + ] + expect(message.attachments).to eq(expected_attachments) + end + end + + context 'merge request notes' do + before do + @args[:object_attributes][:note] = 'comment on a merge request' + @args[:object_attributes][:noteable_type] = 'MergeRequest' + @args[:merge_request] = { + id: 1, + iid: 30, + title: "merge request title\ndetails\n" + } + end + it 'returns a message regarding notes on a merge request' do + message = SlackService::NoteMessage.new(@args) + expect(message.pretext).to eq("Test User commented on " \ + "<url|merge request #30> in <somewhere.com|project_name>: " \ + "*merge request title*") + expected_attachments = [ + { + text: "comment on a merge request", + color: color, + } + ] + expect(message.attachments).to eq(expected_attachments) + end + end + + context 'issue notes' do + before do + @args[:object_attributes][:note] = 'comment on an issue' + @args[:object_attributes][:noteable_type] = 'Issue' + @args[:issue] = { + id: 1, + iid: 20, + title: "issue title\ndetails\n" + } + end + + it 'returns a message regarding notes on an issue' do + message = SlackService::NoteMessage.new(@args) + expect(message.pretext).to eq( + "Test User commented on " \ + "<url|issue #20> in <somewhere.com|project_name>: " \ + "*issue title*") + expected_attachments = [ + { + text: "comment on an issue", + color: color, + } + ] + expect(message.attachments).to eq(expected_attachments) + end + end + + context 'project snippet notes' do + before do + @args[:object_attributes][:note] = 'comment on a snippet' + @args[:object_attributes][:noteable_type] = 'Snippet' + @args[:snippet] = { + id: 5, + title: "snippet title\ndetails\n" + } + end + + it 'returns a message regarding notes on a project snippet' do + message = SlackService::NoteMessage.new(@args) + expect(message.pretext).to eq("Test User commented on " \ + "<url|snippet #5> in <somewhere.com|project_name>: " \ + "*snippet title*") + expected_attachments = [ + { + text: "comment on a snippet", + color: color, + } + ] + expect(message.attachments).to eq(expected_attachments) + end + end +end diff --git a/spec/models/project_services/slack_message_spec.rb b/spec/models/project_services/slack_service/push_message_spec.rb index c530fad619b..10963481a12 100644 --- a/spec/models/project_services/slack_message_spec.rb +++ b/spec/models/project_services/slack_service/push_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlackMessage do - subject { SlackMessage.new(args) } +describe SlackService::PushMessage do + subject { SlackService::PushMessage.new(args) } let(:args) { { @@ -25,41 +25,64 @@ describe SlackMessage do end it 'returns a message regarding pushes' do - subject.pretext.should == + expect(subject.pretext).to eq( 'user_name pushed to branch <url/commits/master|master> of '\ '<url|project_name> (<url/compare/before...after|Compare changes>)' - subject.attachments.should == [ + ) + expect(subject.attachments).to eq([ { - text: "<url1|abcdefghi>: message1 - author1\n"\ - "<url2|123456789>: message2 - author2", + text: "<url1|abcdefgh>: message1 - author1\n"\ + "<url2|12345678>: message2 - author2", color: color, } - ] + ]) + end + end + + context 'tag push' do + let(:args) { + { + after: 'after', + before: Gitlab::Git::BLANK_SHA, + project_name: 'project_name', + ref: 'refs/tags/new_tag', + user_name: 'user_name', + project_url: 'url' + } + } + + it 'returns a message regarding pushes' do + expect(subject.pretext).to eq('user_name pushed new tag ' \ + '<url/commits/new_tag|new_tag> to ' \ + '<url|project_name>') + expect(subject.attachments).to be_empty end end context 'new branch' do before do - args[:before] = '000000' + args[:before] = Gitlab::Git::BLANK_SHA end it 'returns a message regarding a new branch' do - subject.pretext.should == + expect(subject.pretext).to eq( 'user_name pushed new branch <url/commits/master|master> to '\ '<url|project_name>' - subject.attachments.should be_empty + ) + expect(subject.attachments).to be_empty end end context 'removed branch' do before do - args[:after] = '000000' + args[:after] = Gitlab::Git::BLANK_SHA end it 'returns a message regarding a removed branch' do - subject.pretext.should == + expect(subject.pretext).to eq( 'user_name removed branch master from <url|project_name>' - subject.attachments.should be_empty + ) + expect(subject.attachments).to be_empty end end end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index 34594072409..c36506644b3 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -2,22 +2,27 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' describe SlackService do describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe "Validations" do @@ -26,7 +31,7 @@ describe SlackService do subject.active = true end - it { should validate_presence_of :webhook } + it { is_expected.to validate_presence_of :webhook } end end @@ -34,8 +39,10 @@ describe SlackService do let(:slack) { SlackService.new } let(:user) { create(:user) } let(:project) { create(:project) } - let(:sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } + let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } + let(:username) { 'slack_username' } + let(:channel) { 'slack_channel' } before do slack.stub( @@ -46,12 +53,118 @@ describe SlackService do ) WebMock.stub_request(:post, webhook_url) + + opts = { + title: 'Awesome issue', + description: 'please fix' + } + + issue_service = Issues::CreateService.new(project, user, opts) + @issue = issue_service.execute + @issues_sample_data = issue_service.hook_data(@issue, 'open') + + opts = { + title: 'Awesome merge_request', + description: 'please fix', + source_branch: 'stable', + target_branch: 'master' + } + merge_service = MergeRequests::CreateService.new(project, + user, opts) + @merge_request = merge_service.execute + @merge_sample_data = merge_service.hook_data(@merge_request, + 'open') + end + + it "should call Slack API for push events" do + slack.execute(push_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it "should call Slack API for issue events" do + slack.execute(@issues_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it "should call Slack API for merge requests events" do + slack.execute(@merge_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it 'should use the username as an option for slack when configured' do + slack.stub(username: username) + expect(Slack::Notifier).to receive(:new). + with(webhook_url, username: username). + and_return( + double(:slack_service).as_null_object + ) + slack.execute(push_sample_data) + end + + it 'should use the channel as an option when it is configured' do + slack.stub(channel: channel) + expect(Slack::Notifier).to receive(:new). + with(webhook_url, channel: channel). + and_return( + double(:slack_service).as_null_object + ) + slack.execute(push_sample_data) + end + end + + describe "Note events" do + let(:slack) { SlackService.new } + let(:user) { create(:user) } + let(:project) { create(:project, creator_id: user.id) } + let(:issue) { create(:issue, project: project) } + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let(:snippet) { create(:project_snippet, project: project) } + let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } + let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") } + let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")} + let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") } + let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } + + before do + slack.stub( + project: project, + project_id: project.id, + service_hook: true, + webhook: webhook_url + ) + + WebMock.stub_request(:post, webhook_url) + end + + it "should call Slack API for commit comment events" do + data = Gitlab::NoteDataBuilder.build(commit_note, user) + slack.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it "should call Slack API for merge request comment events" do + data = Gitlab::NoteDataBuilder.build(merge_request_note, user) + slack.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + + it "should call Slack API for issue comment events" do + data = Gitlab::NoteDataBuilder.build(issue_note, user) + slack.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once end - it "should call Slack API" do - slack.execute(sample_data) + it "should call Slack API for snippet comment events" do + data = Gitlab::NoteDataBuilder.build(snippet_note, user) + slack.execute(data) - WebMock.should have_requested(:post, webhook_url).once + expect(WebMock).to have_requested(:post, webhook_url).once end end end diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index a6e1d9eef50..3e8f106d27f 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -19,13 +19,13 @@ require 'spec_helper' describe ProjectSnippet do describe "Associations" do - it { should belong_to(:project) } + it { is_expected.to belong_to(:project) } end describe "Mass assignment" do end describe "Validation" do - it { should validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:project) } end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e2197420018..879a63dd9f9 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -33,25 +33,25 @@ require 'spec_helper' describe Project do describe 'Associations' do - it { should belong_to(:group) } - it { should belong_to(:namespace) } - it { should belong_to(:creator).class_name('User') } - it { should have_many(:users) } - it { should have_many(:events).dependent(:destroy) } - it { should have_many(:merge_requests).dependent(:destroy) } - it { should have_many(:issues).dependent(:destroy) } - it { should have_many(:milestones).dependent(:destroy) } - it { should have_many(:project_members).dependent(:destroy) } - it { should have_many(:notes).dependent(:destroy) } - it { should have_many(:snippets).class_name('ProjectSnippet').dependent(:destroy) } - it { should have_many(:deploy_keys_projects).dependent(:destroy) } - it { should have_many(:deploy_keys) } - it { should have_many(:hooks).dependent(:destroy) } - it { should have_many(:protected_branches).dependent(:destroy) } - it { should have_one(:forked_project_link).dependent(:destroy) } - it { should have_one(:slack_service).dependent(:destroy) } - it { should have_one(:pushover_service).dependent(:destroy) } - it { should have_one(:asana_service).dependent(:destroy) } + it { is_expected.to belong_to(:group) } + it { is_expected.to belong_to(:namespace) } + it { is_expected.to belong_to(:creator).class_name('User') } + it { is_expected.to have_many(:users) } + it { is_expected.to have_many(:events).dependent(:destroy) } + it { is_expected.to have_many(:merge_requests).dependent(:destroy) } + it { is_expected.to have_many(:issues).dependent(:destroy) } + it { is_expected.to have_many(:milestones).dependent(:destroy) } + it { is_expected.to have_many(:project_members).dependent(:destroy) } + it { is_expected.to have_many(:notes).dependent(:destroy) } + it { is_expected.to have_many(:snippets).class_name('ProjectSnippet').dependent(:destroy) } + it { is_expected.to have_many(:deploy_keys_projects).dependent(:destroy) } + it { is_expected.to have_many(:deploy_keys) } + it { is_expected.to have_many(:hooks).dependent(:destroy) } + it { is_expected.to have_many(:protected_branches).dependent(:destroy) } + it { is_expected.to have_one(:forked_project_link).dependent(:destroy) } + it { is_expected.to have_one(:slack_service).dependent(:destroy) } + it { is_expected.to have_one(:pushover_service).dependent(:destroy) } + it { is_expected.to have_one(:asana_service).dependent(:destroy) } end describe 'Mass assignment' do @@ -60,50 +60,50 @@ describe Project do describe 'Validation' do let!(:project) { create(:project) } - it { should validate_presence_of(:name) } - it { should validate_uniqueness_of(:name).scoped_to(:namespace_id) } - it { should ensure_length_of(:name).is_within(0..255) } + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) } + it { is_expected.to ensure_length_of(:name).is_within(0..255) } - it { should validate_presence_of(:path) } - it { should validate_uniqueness_of(:path).scoped_to(:namespace_id) } - it { should ensure_length_of(:path).is_within(0..255) } - it { should ensure_length_of(:description).is_within(0..2000) } - it { should validate_presence_of(:creator) } - it { should ensure_length_of(:issues_tracker_id).is_within(0..255) } - it { should validate_presence_of(:namespace) } + it { is_expected.to validate_presence_of(:path) } + it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) } + it { is_expected.to ensure_length_of(:path).is_within(0..255) } + it { is_expected.to ensure_length_of(:description).is_within(0..2000) } + it { is_expected.to validate_presence_of(:creator) } + it { is_expected.to ensure_length_of(:issues_tracker_id).is_within(0..255) } + it { is_expected.to validate_presence_of(:namespace) } it 'should not allow new projects beyond user limits' do project2 = build(:project) - project2.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object) - project2.should_not be_valid - project2.errors[:limit_reached].first.should match(/Your project limit is 0/) + allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object) + expect(project2).not_to be_valid + expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/) end end describe 'Respond to' do - it { should respond_to(:url_to_repo) } - it { should respond_to(:repo_exists?) } - it { should respond_to(:satellite) } - it { should respond_to(:update_merge_requests) } - it { should respond_to(:execute_hooks) } - it { should respond_to(:name_with_namespace) } - it { should respond_to(:owner) } - it { should respond_to(:path_with_namespace) } + it { is_expected.to respond_to(:url_to_repo) } + it { is_expected.to respond_to(:repo_exists?) } + it { is_expected.to respond_to(:satellite) } + it { is_expected.to respond_to(:update_merge_requests) } + it { is_expected.to respond_to(:execute_hooks) } + it { is_expected.to respond_to(:name_with_namespace) } + it { is_expected.to respond_to(:owner) } + it { is_expected.to respond_to(:path_with_namespace) } end it 'should return valid url to repo' do project = Project.new(path: 'somewhere') - project.url_to_repo.should == Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git' + expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git') end it 'returns the full web URL for this repo' do project = Project.new(path: 'somewhere') - project.web_url.should == "#{Gitlab.config.gitlab.url}/somewhere" + expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/somewhere") end it 'returns the web URL without the protocol for this repo' do project = Project.new(path: 'somewhere') - project.web_url_without_protocol.should == "#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere" + expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere") end describe 'last_activity methods' do @@ -113,18 +113,18 @@ describe Project do describe 'last_activity' do it 'should alias last_activity to last_event' do project.stub(last_event: last_event) - project.last_activity.should == last_event + expect(project.last_activity).to eq(last_event) end end describe 'last_activity_date' do it 'returns the creation date of the project\'s last event if present' do last_activity_event = create(:event, project: project) - project.last_activity_at.to_i.should == last_event.created_at.to_i + expect(project.last_activity_at.to_i).to eq(last_event.created_at.to_i) end it 'returns the project\'s last update date if it has no events' do - project.last_activity_date.should == project.updated_at + expect(project.last_activity_date).to eq(project.updated_at) end end end @@ -139,13 +139,13 @@ describe Project do it 'should close merge request if last commit from source branch was pushed to target branch' do project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.target_branch}", key.user) merge_request.reload - merge_request.merged?.should be_true + expect(merge_request.merged?).to be_truthy end it 'should update merge request commits with new one if pushed to source branch' do project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.source_branch}", key.user) merge_request.reload - merge_request.last_commit.id.should == commit_id + expect(merge_request.last_commit.id).to eq(commit_id) end end @@ -156,8 +156,8 @@ describe Project do @project = create(:project, name: 'gitlabhq', namespace: @group) end - it { Project.find_with_namespace('gitlab/gitlabhq').should == @project } - it { Project.find_with_namespace('gitlab-ci').should be_nil } + it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) } + it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil } end end @@ -168,7 +168,7 @@ describe Project do @project = create(:project, name: 'gitlabhq', namespace: @group) end - it { @project.to_param.should == 'gitlab/gitlabhq' } + it { expect(@project.to_param).to eq('gitlabhq') } end end @@ -176,7 +176,7 @@ describe Project do let(:project) { create(:project) } it 'should return valid repo' do - project.repository.should be_kind_of(Repository) + expect(project.repository).to be_kind_of(Repository) end end @@ -187,15 +187,15 @@ describe Project do let(:ext_project) { create(:redmine_project) } it 'should be true or if used internal tracker and issue exists' do - project.issue_exists?(existed_issue.iid).should be_true + expect(project.issue_exists?(existed_issue.iid)).to be_truthy end it 'should be false or if used internal tracker and issue not exists' do - project.issue_exists?(not_existed_issue.iid).should be_false + expect(project.issue_exists?(not_existed_issue.iid)).to be_falsey end it 'should always be true if used other tracker' do - ext_project.issue_exists?(rand(100)).should be_true + expect(ext_project.issue_exists?(rand(100))).to be_truthy end end @@ -204,11 +204,11 @@ describe Project do let(:ext_project) { create(:redmine_project) } it "should be true if used internal tracker" do - project.default_issues_tracker?.should be_true + expect(project.default_issues_tracker?).to be_truthy end it "should be false if used other tracker" do - ext_project.default_issues_tracker?.should be_false + expect(ext_project.default_issues_tracker?).to be_falsey end end @@ -217,19 +217,19 @@ describe Project do let(:ext_project) { create(:redmine_project) } it 'should be true for projects with external issues tracker if issues enabled' do - ext_project.can_have_issues_tracker_id?.should be_true + expect(ext_project.can_have_issues_tracker_id?).to be_truthy end it 'should be false for projects with internal issue tracker if issues enabled' do - project.can_have_issues_tracker_id?.should be_false + expect(project.can_have_issues_tracker_id?).to be_falsey end it 'should be always false if issues disabled' do project.issues_enabled = false ext_project.issues_enabled = false - project.can_have_issues_tracker_id?.should be_false - ext_project.can_have_issues_tracker_id?.should be_false + expect(project.can_have_issues_tracker_id?).to be_falsey + expect(ext_project.can_have_issues_tracker_id?).to be_falsey end end @@ -240,8 +240,8 @@ describe Project do project.protected_branches.create(name: 'master') end - it { project.open_branches.map(&:name).should include('feature') } - it { project.open_branches.map(&:name).should_not include('master') } + it { expect(project.open_branches.map(&:name)).to include('feature') } + it { expect(project.open_branches.map(&:name)).not_to include('master') } end describe '#star_count' do @@ -318,12 +318,43 @@ describe Project do it 'should be true if avatar is image' do project.update_attribute(:avatar, 'uploads/avatar.png') - project.avatar_type.should be_true + expect(project.avatar_type).to be_truthy end it 'should be false if avatar is html page' do project.update_attribute(:avatar, 'uploads/avatar.html') - project.avatar_type.should == ['only images allowed'] + expect(project.avatar_type).to eq(['only images allowed']) + end + end + + describe :avatar_url do + subject { project.avatar_url } + + let(:project) { create(:project) } + + context 'When avatar file is uploaded' do + before do + project.update_columns(avatar: 'uploads/avatar.png') + allow(project.avatar).to receive(:present?) { true } + end + + let(:avatar_path) do + "/uploads/project/avatar/#{project.id}/uploads/avatar.png" + end + + it { should eq "http://localhost#{avatar_path}" } + end + + context 'When avatar file in git' do + before do + allow(project).to receive(:avatar_in_git) { true } + end + + let(:avatar_path) do + "/#{project.namespace.name}/#{project.path}/avatar" + end + + it { should eq "http://localhost#{avatar_path}" } end end end diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index bbf50b654f4..19201cc15a7 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -16,19 +16,19 @@ describe ProjectTeam do end describe 'members collection' do - it { project.team.masters.should include(master) } - it { project.team.masters.should_not include(guest) } - it { project.team.masters.should_not include(reporter) } - it { project.team.masters.should_not include(nonmember) } + it { expect(project.team.masters).to include(master) } + it { expect(project.team.masters).not_to include(guest) } + it { expect(project.team.masters).not_to include(reporter) } + it { expect(project.team.masters).not_to include(nonmember) } end describe 'access methods' do - it { project.team.master?(master).should be_true } - it { project.team.master?(guest).should be_false } - it { project.team.master?(reporter).should be_false } - it { project.team.master?(nonmember).should be_false } - it { project.team.member?(nonmember).should be_false } - it { project.team.member?(guest).should be_true } + it { expect(project.team.master?(master)).to be_truthy } + it { expect(project.team.master?(guest)).to be_falsey } + it { expect(project.team.master?(reporter)).to be_falsey } + it { expect(project.team.master?(nonmember)).to be_falsey } + it { expect(project.team.member?(nonmember)).to be_falsey } + it { expect(project.team.member?(guest)).to be_truthy } end end @@ -49,21 +49,21 @@ describe ProjectTeam do end describe 'members collection' do - it { project.team.reporters.should include(reporter) } - it { project.team.masters.should include(master) } - it { project.team.masters.should include(guest) } - it { project.team.masters.should_not include(reporter) } - it { project.team.masters.should_not include(nonmember) } + it { expect(project.team.reporters).to include(reporter) } + it { expect(project.team.masters).to include(master) } + it { expect(project.team.masters).to include(guest) } + it { expect(project.team.masters).not_to include(reporter) } + it { expect(project.team.masters).not_to include(nonmember) } end describe 'access methods' do - it { project.team.reporter?(reporter).should be_true } - it { project.team.master?(master).should be_true } - it { project.team.master?(guest).should be_true } - it { project.team.master?(reporter).should be_false } - it { project.team.master?(nonmember).should be_false } - it { project.team.member?(nonmember).should be_false } - it { project.team.member?(guest).should be_true } + it { expect(project.team.reporter?(reporter)).to be_truthy } + it { expect(project.team.master?(master)).to be_truthy } + it { expect(project.team.master?(guest)).to be_truthy } + it { expect(project.team.master?(reporter)).to be_falsey } + it { expect(project.team.master?(nonmember)).to be_falsey } + it { expect(project.team.member?(nonmember)).to be_falsey } + it { expect(project.team.member?(guest)).to be_truthy } end end end diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index e4ee2fc5b13..2acdb7dfddc 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -12,19 +12,19 @@ describe ProjectWiki do describe "#path_with_namespace" do it "returns the project path with namespace with the .wiki extension" do - subject.path_with_namespace.should == project.path_with_namespace + ".wiki" + expect(subject.path_with_namespace).to eq(project.path_with_namespace + ".wiki") end end describe "#url_to_repo" do it "returns the correct ssh url to the repo" do - subject.url_to_repo.should == gitlab_shell.url_to_repo(subject.path_with_namespace) + expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.path_with_namespace)) end end describe "#ssh_url_to_repo" do it "equals #url_to_repo" do - subject.ssh_url_to_repo.should == subject.url_to_repo + expect(subject.ssh_url_to_repo).to eq(subject.url_to_repo) end end @@ -32,21 +32,21 @@ describe ProjectWiki do it "provides the full http url to the repo" do gitlab_url = Gitlab.config.gitlab.url repo_http_url = "#{gitlab_url}/#{subject.path_with_namespace}.git" - subject.http_url_to_repo.should == repo_http_url + expect(subject.http_url_to_repo).to eq(repo_http_url) end end describe "#wiki" do it "contains a Gollum::Wiki instance" do - subject.wiki.should be_a Gollum::Wiki + expect(subject.wiki).to be_a Gollum::Wiki end it "creates a new wiki repo if one does not yet exist" do - project_wiki.create_page("index", "test content").should be_true + expect(project_wiki.create_page("index", "test content")).to be_truthy end it "raises CouldNotCreateWikiError if it can't create the wiki repository" do - project_wiki.stub(:init_repo).and_return(false) + allow(project_wiki).to receive(:init_repo).and_return(false) expect { project_wiki.send(:create_repo!) }.to raise_exception(ProjectWiki::CouldNotCreateWikiError) end end @@ -54,21 +54,27 @@ describe ProjectWiki do describe "#empty?" do context "when the wiki repository is empty" do before do - Gitlab::Shell.any_instance.stub(:add_repository) do + allow_any_instance_of(Gitlab::Shell).to receive(:add_repository) do create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git") end - project.stub(:path_with_namespace).and_return("non-existant") + allow(project).to receive(:path_with_namespace).and_return("non-existant") end - its(:empty?) { should be_true } + describe '#empty?' do + subject { super().empty? } + it { is_expected.to be_truthy } + end end context "when the wiki has pages" do before do - create_page("index", "This is an awesome new Gollum Wiki") + project_wiki.create_page("index", "This is an awesome new Gollum Wiki") end - its(:empty?) { should be_false } + describe '#empty?' do + subject { super().empty? } + it { is_expected.to be_falsey } + end end end @@ -83,11 +89,11 @@ describe ProjectWiki do end it "returns an array of WikiPage instances" do - @pages.first.should be_a WikiPage + expect(@pages.first).to be_a WikiPage end it "returns the correct number of pages" do - @pages.count.should == 1 + expect(@pages.count).to eq(1) end end @@ -102,55 +108,55 @@ describe ProjectWiki do it "returns the latest version of the page if it exists" do page = subject.find_page("index page") - page.title.should == "index page" + expect(page.title).to eq("index page") end it "returns nil if the page does not exist" do - subject.find_page("non-existant").should == nil + expect(subject.find_page("non-existant")).to eq(nil) end it "can find a page by slug" do page = subject.find_page("index-page") - page.title.should == "index page" + expect(page.title).to eq("index page") end it "returns a WikiPage instance" do page = subject.find_page("index page") - page.should be_a WikiPage + expect(page).to be_a WikiPage end end describe '#find_file' do before do file = Gollum::File.new(subject.wiki) - Gollum::Wiki.any_instance. - stub(:file).with('image.jpg', 'master', true). + allow_any_instance_of(Gollum::Wiki). + to receive(:file).with('image.jpg', 'master', true). and_return(file) - Gollum::File.any_instance. - stub(:mime_type). + allow_any_instance_of(Gollum::File). + to receive(:mime_type). and_return('image/jpeg') - Gollum::Wiki.any_instance. - stub(:file).with('non-existant', 'master', true). + allow_any_instance_of(Gollum::Wiki). + to receive(:file).with('non-existant', 'master', true). and_return(nil) end after do - Gollum::Wiki.any_instance.unstub(:file) - Gollum::File.any_instance.unstub(:mime_type) + allow_any_instance_of(Gollum::Wiki).to receive(:file).and_call_original + allow_any_instance_of(Gollum::File).to receive(:mime_type).and_call_original end it 'returns the latest version of the file if it exists' do file = subject.find_file('image.jpg') - file.mime_type.should == 'image/jpeg' + expect(file.mime_type).to eq('image/jpeg') end it 'returns nil if the page does not exist' do - subject.find_file('non-existant').should == nil + expect(subject.find_file('non-existant')).to eq(nil) end it 'returns a Gollum::File instance' do file = subject.find_file('image.jpg') - file.should be_a Gollum::File + expect(file).to be_a Gollum::File end end @@ -160,23 +166,23 @@ describe ProjectWiki do end it "creates a new wiki page" do - subject.create_page("test page", "this is content").should_not == false - subject.pages.count.should == 1 + expect(subject.create_page("test page", "this is content")).not_to eq(false) + expect(subject.pages.count).to eq(1) end it "returns false when a duplicate page exists" do subject.create_page("test page", "content") - subject.create_page("test page", "content").should == false + expect(subject.create_page("test page", "content")).to eq(false) end it "stores an error message when a duplicate page exists" do 2.times { subject.create_page("test page", "content") } - subject.error_message.should =~ /Duplicate page:/ + expect(subject.error_message).to match(/Duplicate page:/) end it "sets the correct commit message" do subject.create_page("test page", "some content", :markdown, "commit message") - subject.pages.first.page.version.message.should == "commit message" + expect(subject.pages.first.page.version.message).to eq("commit message") end end @@ -193,11 +199,11 @@ describe ProjectWiki do end it "updates the content of the page" do - @page.raw_data.should == "some other content" + expect(@page.raw_data).to eq("some other content") end it "sets the correct commit message" do - @page.version.message.should == "updated page" + expect(@page.version.message).to eq("updated page") end end @@ -209,7 +215,7 @@ describe ProjectWiki do it "deletes the page" do subject.delete_page(@page) - subject.pages.count.should == 0 + expect(subject.pages.count).to eq(0) end end diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index b0f57e8a206..1e6937b536c 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -14,14 +14,14 @@ require 'spec_helper' describe ProtectedBranch do describe 'Associations' do - it { should belong_to(:project) } + it { is_expected.to belong_to(:project) } end describe "Mass assignment" do end describe 'Validation' do - it { should validate_presence_of(:project) } - it { should validate_presence_of(:name) } + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:name) } end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 6c3e221f343..b3a38f6c5b9 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -8,14 +8,37 @@ describe Repository do describe :branch_names_contains do subject { repository.branch_names_contains(sample_commit.id) } - it { should include('master') } - it { should_not include('feature') } - it { should_not include('fix') } + it { is_expected.to include('master') } + it { is_expected.not_to include('feature') } + it { is_expected.not_to include('fix') } end describe :last_commit_for_path do subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id } - it { should eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } + it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } + end + + context :timestamps_by_user_log do + before do + Date.stub(:today).and_return(Date.new(2015, 03, 01)) + end + + describe 'single e-mail for user' do + let(:user) { create(:user, email: sample_commit.author_email) } + + subject { repository.timestamps_by_user_log(user) } + + it { is_expected.to eq(["2014-08-06", "2014-07-31", "2014-07-31"]) } + end + + describe 'multiple emails for user' do + let(:email_alias) { create(:email, email: another_sample_commit.author_email) } + let(:user) { create(:user, email: sample_commit.author_email, emails: [email_alias]) } + + subject { repository.timestamps_by_user_log(user) } + + it { is_expected.to eq(["2015-01-10", "2014-08-06", "2014-07-31", "2014-07-31"]) } + end end end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index c96f2b20529..735652aea78 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -2,14 +2,19 @@ # # Table name: services # -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) # require 'spec_helper' @@ -17,8 +22,8 @@ require 'spec_helper' describe Service do describe "Associations" do - it { should belong_to :project } - it { should have_one :service_hook } + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } end describe "Mass assignment" do @@ -40,7 +45,7 @@ describe Service do end describe :can_test do - it { @testable.should == true } + it { expect(@testable).to eq(true) } end end @@ -55,7 +60,32 @@ describe Service do end describe :can_test do - it { @testable.should == true } + it { expect(@testable).to eq(true) } + end + end + end + + describe "Template" do + describe "for pushover service" do + let(:service_template) { + PushoverService.create(template: true, properties: {device: 'MyDevice', sound: 'mic', priority: 4, api_key: '123456789'}) + } + let(:project) { create(:project) } + + describe 'should be prefilled for projects pushover service' do + before do + service_template + project.build_missing_services + end + + it "should have all fields prefilled" do + service = project.pushover_service + expect(service.template).to eq(false) + expect(service.device).to eq('MyDevice') + expect(service.sound).to eq('mic') + expect(service.priority).to eq(4) + expect(service.api_key).to eq('123456789') + end end end end diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 1ef2c512c1f..e37dcc75230 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -19,22 +19,22 @@ require 'spec_helper' describe Snippet do describe "Associations" do - it { should belong_to(:author).class_name('User') } - it { should have_many(:notes).dependent(:destroy) } + it { is_expected.to belong_to(:author).class_name('User') } + it { is_expected.to have_many(:notes).dependent(:destroy) } end describe "Mass assignment" do end describe "Validation" do - it { should validate_presence_of(:author) } + it { is_expected.to validate_presence_of(:author) } - it { should validate_presence_of(:title) } - it { should ensure_length_of(:title).is_within(0..255) } + it { is_expected.to validate_presence_of(:title) } + it { is_expected.to ensure_length_of(:title).is_within(0..255) } - it { should validate_presence_of(:file_name) } - it { should ensure_length_of(:title).is_within(0..255) } + it { is_expected.to validate_presence_of(:file_name) } + it { is_expected.to ensure_length_of(:title).is_within(0..255) } - it { should validate_presence_of(:content) } + it { is_expected.to validate_presence_of(:content) } end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 629d51b960d..10e90cae143 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,79 +2,85 @@ # # Table name: users # -# id :integer not null, primary key -# email :string(255) default(""), not null -# encrypted_password :string(255) default(""), not null -# reset_password_token :string(255) -# reset_password_sent_at :datetime -# remember_created_at :datetime -# sign_in_count :integer default(0) -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) -# created_at :datetime -# updated_at :datetime -# name :string(255) -# admin :boolean default(FALSE), not null -# projects_limit :integer default(10) -# skype :string(255) default(""), not null -# linkedin :string(255) default(""), not null -# twitter :string(255) default(""), not null -# authentication_token :string(255) -# theme_id :integer default(1), not null -# bio :string(255) -# failed_attempts :integer default(0) -# locked_at :datetime -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null -# password_expires_at :datetime -# created_by_id :integer -# avatar :string(255) -# confirmation_token :string(255) -# confirmed_at :datetime -# confirmation_sent_at :datetime -# unconfirmed_email :string(255) -# hide_no_ssh_key :boolean default(FALSE) -# website_url :string(255) default(""), not null -# last_credential_check_at :datetime -# github_access_token :string(255) +# id :integer not null, primary key +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# reset_password_token :string(255) +# reset_password_sent_at :datetime +# remember_created_at :datetime +# sign_in_count :integer default(0) +# current_sign_in_at :datetime +# last_sign_in_at :datetime +# current_sign_in_ip :string(255) +# last_sign_in_ip :string(255) +# created_at :datetime +# updated_at :datetime +# name :string(255) +# admin :boolean default(FALSE), not null +# projects_limit :integer default(10) +# skype :string(255) default(""), not null +# linkedin :string(255) default(""), not null +# twitter :string(255) default(""), not null +# authentication_token :string(255) +# theme_id :integer default(1), not null +# bio :string(255) +# failed_attempts :integer default(0) +# locked_at :datetime +# username :string(255) +# can_create_group :boolean default(TRUE), not null +# can_create_team :boolean default(TRUE), not null +# state :string(255) +# color_scheme_id :integer default(1), not null +# notification_level :integer default(1), not null +# password_expires_at :datetime +# created_by_id :integer +# last_credential_check_at :datetime +# avatar :string(255) +# confirmation_token :string(255) +# confirmed_at :datetime +# confirmation_sent_at :datetime +# unconfirmed_email :string(255) +# hide_no_ssh_key :boolean default(FALSE) +# website_url :string(255) default(""), not null +# github_access_token :string(255) +# gitlab_access_token :string(255) +# notification_email :string(255) +# hide_no_password :boolean default(FALSE) +# password_automatically_set :boolean default(FALSE) +# bitbucket_access_token :string(255) +# bitbucket_access_token_secret :string(255) # require 'spec_helper' describe User do describe "Associations" do - it { should have_one(:namespace) } - it { should have_many(:snippets).class_name('Snippet').dependent(:destroy) } - it { should have_many(:project_members).dependent(:destroy) } - it { should have_many(:groups) } - it { should have_many(:keys).dependent(:destroy) } - it { should have_many(:events).class_name('Event').dependent(:destroy) } - it { should have_many(:recent_events).class_name('Event') } - it { should have_many(:issues).dependent(:destroy) } - it { should have_many(:notes).dependent(:destroy) } - it { should have_many(:assigned_issues).dependent(:destroy) } - it { should have_many(:merge_requests).dependent(:destroy) } - it { should have_many(:assigned_merge_requests).dependent(:destroy) } - it { should have_many(:identities).dependent(:destroy) } + it { is_expected.to have_one(:namespace) } + it { is_expected.to have_many(:snippets).class_name('Snippet').dependent(:destroy) } + it { is_expected.to have_many(:project_members).dependent(:destroy) } + it { is_expected.to have_many(:groups) } + it { is_expected.to have_many(:keys).dependent(:destroy) } + it { is_expected.to have_many(:events).class_name('Event').dependent(:destroy) } + it { is_expected.to have_many(:recent_events).class_name('Event') } + it { is_expected.to have_many(:issues).dependent(:destroy) } + it { is_expected.to have_many(:notes).dependent(:destroy) } + it { is_expected.to have_many(:assigned_issues).dependent(:destroy) } + it { is_expected.to have_many(:merge_requests).dependent(:destroy) } + it { is_expected.to have_many(:assigned_merge_requests).dependent(:destroy) } + it { is_expected.to have_many(:identities).dependent(:destroy) } end describe "Mass assignment" do end describe 'validations' do - it { should validate_presence_of(:username) } - it { should validate_presence_of(:projects_limit) } - it { should validate_numericality_of(:projects_limit) } - it { should allow_value(0).for(:projects_limit) } - it { should_not allow_value(-1).for(:projects_limit) } + it { is_expected.to validate_presence_of(:username) } + it { is_expected.to validate_presence_of(:projects_limit) } + it { is_expected.to validate_numericality_of(:projects_limit) } + it { is_expected.to allow_value(0).for(:projects_limit) } + it { is_expected.not_to allow_value(-1).for(:projects_limit) } - it { should ensure_length_of(:bio).is_within(0..255) } + it { is_expected.to ensure_length_of(:bio).is_within(0..255) } describe 'email' do it 'accepts info@example.com' do @@ -110,34 +116,34 @@ describe User do end describe "Respond to" do - it { should respond_to(:is_admin?) } - it { should respond_to(:name) } - it { should respond_to(:private_token) } + it { is_expected.to respond_to(:is_admin?) } + it { is_expected.to respond_to(:name) } + it { is_expected.to respond_to(:private_token) } end describe '#generate_password' do it "should execute callback when force_random_password specified" do user = build(:user, force_random_password: true) - user.should_receive(:generate_password) + expect(user).to receive(:generate_password) user.save end it "should not generate password by default" do user = create(:user, password: 'abcdefghe') - user.password.should == 'abcdefghe' + expect(user.password).to eq('abcdefghe') end it "should generate password when forcing random password" do - Devise.stub(:friendly_token).and_return('123456789') + allow(Devise).to receive(:friendly_token).and_return('123456789') user = create(:user, password: 'abcdefg', force_random_password: true) - user.password.should == '12345678' + expect(user.password).to eq('12345678') end end describe 'authentication token' do it "should have authentication token" do user = create(:user) - user.authentication_token.should_not be_blank + expect(user.authentication_token).not_to be_blank end end @@ -152,15 +158,15 @@ describe User do @project_3.team << [@user, :developer] end - it { @user.authorized_projects.should include(@project) } - it { @user.authorized_projects.should include(@project_2) } - it { @user.authorized_projects.should include(@project_3) } - it { @user.owned_projects.should include(@project) } - it { @user.owned_projects.should_not include(@project_2) } - it { @user.owned_projects.should_not include(@project_3) } - it { @user.personal_projects.should include(@project) } - it { @user.personal_projects.should_not include(@project_2) } - it { @user.personal_projects.should_not include(@project_3) } + it { expect(@user.authorized_projects).to include(@project) } + it { expect(@user.authorized_projects).to include(@project_2) } + it { expect(@user.authorized_projects).to include(@project_3) } + it { expect(@user.owned_projects).to include(@project) } + it { expect(@user.owned_projects).not_to include(@project_2) } + it { expect(@user.owned_projects).not_to include(@project_3) } + it { expect(@user.personal_projects).to include(@project) } + it { expect(@user.personal_projects).not_to include(@project_2) } + it { expect(@user.personal_projects).not_to include(@project_3) } end describe 'groups' do @@ -170,9 +176,9 @@ describe User do @group.add_owner(@user) end - it { @user.several_namespaces?.should be_true } - it { @user.authorized_groups.should == [@group] } - it { @user.owned_groups.should == [@group] } + it { expect(@user.several_namespaces?).to be_truthy } + it { expect(@user.authorized_groups).to eq([@group]) } + it { expect(@user.owned_groups).to eq([@group]) } end describe 'group multiple owners' do @@ -185,7 +191,7 @@ describe User do @group.add_user(@user2, GroupMember::OWNER) end - it { @user2.several_namespaces?.should be_true } + it { expect(@user2.several_namespaces?).to be_truthy } end describe 'namespaced' do @@ -194,7 +200,7 @@ describe User do @project = create :project, namespace: @user.namespace end - it { @user.several_namespaces?.should be_false } + it { expect(@user.several_namespaces?).to be_falsey } end describe 'blocking user' do @@ -202,7 +208,7 @@ describe User do it "should block user" do user.block - user.blocked?.should be_true + expect(user.blocked?).to be_truthy end end @@ -214,10 +220,10 @@ describe User do @blocked = create :user, state: :blocked end - it { User.filter("admins").should == [@admin] } - it { User.filter("blocked").should == [@blocked] } - it { User.filter("wop").should include(@user, @admin, @blocked) } - it { User.filter(nil).should include(@user, @admin) } + it { expect(User.filter("admins")).to eq([@admin]) } + it { expect(User.filter("blocked")).to eq([@blocked]) } + it { expect(User.filter("wop")).to include(@user, @admin, @blocked) } + it { expect(User.filter(nil)).to include(@user, @admin) } end describe :not_in_project do @@ -227,27 +233,27 @@ describe User do @project = create :project end - it { User.not_in_project(@project).should include(@user, @project.owner) } + it { expect(User.not_in_project(@project)).to include(@user, @project.owner) } end describe 'user creation' do describe 'normal user' do let(:user) { create(:user, name: 'John Smith') } - it { user.is_admin?.should be_false } - it { user.require_ssh_key?.should be_true } - it { user.can_create_group?.should be_true } - it { user.can_create_project?.should be_true } - it { user.first_name.should == 'John' } + it { expect(user.is_admin?).to be_falsey } + it { expect(user.require_ssh_key?).to be_truthy } + it { expect(user.can_create_group?).to be_truthy } + it { expect(user.can_create_project?).to be_truthy } + it { expect(user.first_name).to eq('John') } end describe 'with defaults' do let(:user) { User.new } it "should apply defaults to user" do - user.projects_limit.should == Gitlab.config.gitlab.default_projects_limit - user.can_create_group.should == Gitlab.config.gitlab.default_can_create_group - user.theme_id.should == Gitlab.config.gitlab.default_theme + expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit) + expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group) + expect(user.theme_id).to eq(Gitlab.config.gitlab.default_theme) end end @@ -255,9 +261,9 @@ describe User do let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true, theme_id: Gitlab::Theme::BASIC) } it "should apply defaults to user" do - user.projects_limit.should == 123 - user.can_create_group.should be_false - user.theme_id.should == Gitlab::Theme::BASIC + expect(user.projects_limit).to eq(123) + expect(user.can_create_group).to be_falsey + expect(user.theme_id).to eq(Gitlab::Theme::BASIC) end end end @@ -267,12 +273,12 @@ describe User do let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') } it "should be case insensitive" do - User.search(user1.username.upcase).to_a.should == [user1] - User.search(user1.username.downcase).to_a.should == [user1] - User.search(user2.username.upcase).to_a.should == [user2] - User.search(user2.username.downcase).to_a.should == [user2] - User.search(user1.username.downcase).to_a.count.should == 2 - User.search(user2.username.downcase).to_a.count.should == 1 + expect(User.search(user1.username.upcase).to_a).to eq([user1]) + expect(User.search(user1.username.downcase).to_a).to eq([user1]) + expect(User.search(user2.username.upcase).to_a).to eq([user2]) + expect(User.search(user2.username.downcase).to_a).to eq([user2]) + expect(User.search(user1.username.downcase).to_a.count).to eq(2) + expect(User.search(user2.username.downcase).to_a.count).to eq(1) end end @@ -280,10 +286,10 @@ describe User do let(:user1) { create(:user, username: 'foo') } it "should get the correct user" do - User.by_username_or_id(user1.id).should == user1 - User.by_username_or_id('foo').should == user1 - User.by_username_or_id(-1).should be_nil - User.by_username_or_id('bar').should be_nil + expect(User.by_username_or_id(user1.id)).to eq(user1) + expect(User.by_username_or_id('foo')).to eq(user1) + expect(User.by_username_or_id(-1)).to be_nil + expect(User.by_username_or_id('bar')).to be_nil end end @@ -301,14 +307,24 @@ describe User do end end + describe ".clean_username" do + + let!(:user) { create(:user, username: "johngitlab-etc") } + let!(:namespace) { create(:namespace, path: "JohnGitLab-etc1") } + + it "cleans a username and makes sure it's available" do + expect(User.clean_username("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2") + end + end + describe 'all_ssh_keys' do - it { should have_many(:keys).dependent(:destroy) } + it { is_expected.to have_many(:keys).dependent(:destroy) } it "should have all ssh keys" do user = create :user key = create :key, key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD33bWLBxu48Sev9Fert1yzEO4WGcWglWF7K/AwblIUFselOt/QdOL9DSjpQGxLagO1s9wl53STIO8qGS4Ms0EJZyIXOEFMjFJ5xmjSy+S37By4sG7SsltQEHMxtbtFOaW5LV2wCrX+rUsRNqLMamZjgjcPO0/EgGCXIGMAYW4O7cwGZdXWYIhQ1Vwy+CsVMDdPkPgBXqK7nR/ey8KMs8ho5fMNgB5hBw/AL9fNGhRw3QTD6Q12Nkhl4VZES2EsZqlpNnJttnPdp847DUsT6yuLRlfiQfz5Cn9ysHFdXObMN5VYIiPFwHeYCZp1X2S4fDZooRE8uOLTfxWHPXwrhqSH", user_id: user.id - user.all_ssh_keys.should include(key.key) + expect(user.all_ssh_keys).to include(key.key) end end @@ -317,12 +333,12 @@ describe User do it "should be true if avatar is image" do user.update_attribute(:avatar, 'uploads/avatar.png') - user.avatar_type.should be_true + expect(user.avatar_type).to be_truthy end it "should be false if avatar is html page" do user.update_attribute(:avatar, 'uploads/avatar.html') - user.avatar_type.should == ["only images allowed"] + expect(user.avatar_type).to eq(["only images allowed"]) end end @@ -333,7 +349,7 @@ describe User do # Create a condition which would otherwise cause 'true' to be returned user.stub(ldap_user?: true) user.last_credential_check_at = nil - expect(user.requires_ldap_check?).to be_false + expect(user.requires_ldap_check?).to be_falsey end context 'when LDAP is enabled' do @@ -341,7 +357,7 @@ describe User do it 'is false for non-LDAP users' do user.stub(ldap_user?: false) - expect(user.requires_ldap_check?).to be_false + expect(user.requires_ldap_check?).to be_falsey end context 'and when the user is an LDAP user' do @@ -349,12 +365,12 @@ describe User do it 'is true when the user has never had an LDAP check before' do user.last_credential_check_at = nil - expect(user.requires_ldap_check?).to be_true + expect(user.requires_ldap_check?).to be_truthy end it 'is true when the last LDAP check happened over 1 hour ago' do user.last_credential_check_at = 2.hours.ago - expect(user.requires_ldap_check?).to be_true + expect(user.requires_ldap_check?).to be_truthy end end end @@ -363,24 +379,24 @@ describe User do describe :ldap_user? do it "is true if provider name starts with ldap" do user = create(:omniauth_user, provider: 'ldapmain') - expect( user.ldap_user? ).to be_true + expect( user.ldap_user? ).to be_truthy end it "is false for other providers" do user = create(:omniauth_user, provider: 'other-provider') - expect( user.ldap_user? ).to be_false + expect( user.ldap_user? ).to be_falsey end it "is false if no extern_uid is provided" do user = create(:omniauth_user, extern_uid: nil) - expect( user.ldap_user? ).to be_false + expect( user.ldap_user? ).to be_falsey end end describe :ldap_identity do it "returns ldap identity" do user = create :omniauth_user - user.ldap_identity.provider.should_not be_empty + expect(user.ldap_identity.provider).not_to be_empty end end @@ -434,24 +450,24 @@ describe User do project1 = create :project, :public project2 = create :project, :public - expect(user.starred?(project1)).to be_false - expect(user.starred?(project2)).to be_false + expect(user.starred?(project1)).to be_falsey + expect(user.starred?(project2)).to be_falsey star1 = UsersStarProject.create!(project: project1, user: user) - expect(user.starred?(project1)).to be_true - expect(user.starred?(project2)).to be_false + expect(user.starred?(project1)).to be_truthy + expect(user.starred?(project2)).to be_falsey star2 = UsersStarProject.create!(project: project2, user: user) - expect(user.starred?(project1)).to be_true - expect(user.starred?(project2)).to be_true + expect(user.starred?(project1)).to be_truthy + expect(user.starred?(project2)).to be_truthy star1.destroy - expect(user.starred?(project1)).to be_false - expect(user.starred?(project2)).to be_true + expect(user.starred?(project1)).to be_falsey + expect(user.starred?(project2)).to be_truthy star2.destroy - expect(user.starred?(project1)).to be_false - expect(user.starred?(project2)).to be_false + expect(user.starred?(project1)).to be_falsey + expect(user.starred?(project2)).to be_falsey end end @@ -460,11 +476,11 @@ describe User do user = create :user project = create :project, :public - expect(user.starred?(project)).to be_false + expect(user.starred?(project)).to be_falsey user.toggle_star(project) - expect(user.starred?(project)).to be_true + expect(user.starred?(project)).to be_truthy user.toggle_star(project) - expect(user.starred?(project)).to be_false + expect(user.starred?(project)).to be_falsey end end @@ -476,23 +492,51 @@ describe User do end it "sorts users as recently_signed_in" do - User.sort('recent_sign_in').first.should == @user + expect(User.sort('recent_sign_in').first).to eq(@user) end it "sorts users as late_signed_in" do - User.sort('oldest_sign_in').first.should == @user1 + expect(User.sort('oldest_sign_in').first).to eq(@user1) end it "sorts users as recently_created" do - User.sort('created_desc').first.should == @user + expect(User.sort('created_desc').first).to eq(@user) end it "sorts users as late_created" do - User.sort('created_asc').first.should == @user1 + expect(User.sort('created_asc').first).to eq(@user1) end it "sorts users by name when nil is passed" do - User.sort(nil).first.should == @user + expect(User.sort(nil).first).to eq(@user) + end + end + + describe "#contributed_projects_ids" do + + subject { create(:user) } + let!(:project1) { create(:project) } + let!(:project2) { create(:project, forked_from_project: project3) } + let!(:project3) { create(:project) } + let!(:merge_request) { create(:merge_request, source_project: project2, target_project: project3, author: subject) } + let!(:push_event) { create(:event, action: Event::PUSHED, project: project1, target: project1, author: subject) } + let!(:merge_event) { create(:event, action: Event::CREATED, project: project3, target: merge_request, author: subject) } + + before do + project1.team << [subject, :master] + project2.team << [subject, :master] + end + + it "includes IDs for projects the user has pushed to" do + expect(subject.contributed_projects_ids).to include(project1.id) + end + + it "includes IDs for projects the user has had merge requests merged into" do + expect(subject.contributed_projects_ids).to include(project3.id) + end + + it "doesn't include IDs for unrelated projects" do + expect(subject.contributed_projects_ids).not_to include(project2.id) end end end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 78877db61b7..f3fd805783f 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -16,27 +16,27 @@ describe WikiPage do end it "sets the slug attribute" do - @wiki_page.slug.should == "test-page" + expect(@wiki_page.slug).to eq("test-page") end it "sets the title attribute" do - @wiki_page.title.should == "test page" + expect(@wiki_page.title).to eq("test page") end it "sets the formatted content attribute" do - @wiki_page.content.should == "test content" + expect(@wiki_page.content).to eq("test content") end it "sets the format attribute" do - @wiki_page.format.should == :markdown + expect(@wiki_page.format).to eq(:markdown) end it "sets the message attribute" do - @wiki_page.message.should == "test commit" + expect(@wiki_page.message).to eq("test commit") end it "sets the version attribute" do - @wiki_page.version.should be_a Gollum::Git::Commit + expect(@wiki_page.version).to be_a Gollum::Git::Commit end end end @@ -48,12 +48,12 @@ describe WikiPage do it "validates presence of title" do subject.attributes.delete(:title) - subject.valid?.should be_false + expect(subject.valid?).to be_falsey end it "validates presence of content" do subject.attributes.delete(:content) - subject.valid?.should be_false + expect(subject.valid?).to be_falsey end end @@ -69,11 +69,11 @@ describe WikiPage do context "with valid attributes" do it "saves the wiki page" do subject.create(@wiki_attr) - wiki.find_page("Index").should_not be_nil + expect(wiki.find_page("Index")).not_to be_nil end it "returns true" do - subject.create(@wiki_attr).should == true + expect(subject.create(@wiki_attr)).to eq(true) end end end @@ -95,7 +95,7 @@ describe WikiPage do end it "returns true" do - @page.update("more content").should be_true + expect(@page.update("more content")).to be_truthy end end end @@ -108,11 +108,11 @@ describe WikiPage do it "should delete the page" do @page.delete - wiki.pages.should be_empty + expect(wiki.pages).to be_empty end it "should return true" do - @page.delete.should == true + expect(@page.delete).to eq(true) end end @@ -128,7 +128,7 @@ describe WikiPage do it "returns an array of all commits for the page" do 3.times { |i| @page.update("content #{i}") } - @page.versions.count.should == 4 + expect(@page.versions.count).to eq(4) end end @@ -144,7 +144,7 @@ describe WikiPage do it "should be replace a hyphen to a space" do @page.title = "Import-existing-repositories-into-GitLab" - @page.title.should == "Import existing repositories into GitLab" + expect(@page.title).to eq("Import existing repositories into GitLab") end end diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb index cc071342d7c..20cb30a39bb 100644 --- a/spec/requests/api/api_helpers_spec.rb +++ b/spec/requests/api/api_helpers_spec.rb @@ -41,33 +41,33 @@ describe API, api: true do describe ".current_user" do it "should return nil for an invalid token" do env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token' - self.class.any_instance.stub(:doorkeeper_guard){ false } - current_user.should be_nil + allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } + expect(current_user).to be_nil end it "should return nil for a user without access" do env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token Gitlab::UserAccess.stub(allowed?: false) - current_user.should be_nil + expect(current_user).to be_nil end it "should leave user as is when sudo not specified" do env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token - current_user.should == user + expect(current_user).to eq(user) clear_env params[API::APIHelpers::PRIVATE_TOKEN_PARAM] = user.private_token - current_user.should == user + expect(current_user).to eq(user) end it "should change current user to sudo when admin" do set_env(admin, user.id) - current_user.should == user + expect(current_user).to eq(user) set_param(admin, user.id) - current_user.should == user + expect(current_user).to eq(user) set_env(admin, user.username) - current_user.should == user + expect(current_user).to eq(user) set_param(admin, user.username) - current_user.should == user + expect(current_user).to eq(user) end it "should throw an error when the current user is not an admin and attempting to sudo" do @@ -83,8 +83,8 @@ describe API, api: true do it "should throw an error when the user cannot be found for a given id" do id = user.id + admin.id - user.id.should_not == id - admin.id.should_not == id + expect(user.id).not_to eq(id) + expect(admin.id).not_to eq(id) set_env(admin, id) expect { current_user }.to raise_error @@ -94,8 +94,8 @@ describe API, api: true do it "should throw an error when the user cannot be found for a given username" do username = "#{user.username}#{admin.username}" - user.username.should_not == username - admin.username.should_not == username + expect(user.username).not_to eq(username) + expect(admin.username).not_to eq(username) set_env(admin, username) expect { current_user }.to raise_error @@ -105,69 +105,69 @@ describe API, api: true do it "should handle sudo's to oneself" do set_env(admin, admin.id) - current_user.should == admin + expect(current_user).to eq(admin) set_param(admin, admin.id) - current_user.should == admin + expect(current_user).to eq(admin) set_env(admin, admin.username) - current_user.should == admin + expect(current_user).to eq(admin) set_param(admin, admin.username) - current_user.should == admin + expect(current_user).to eq(admin) end it "should handle multiple sudo's to oneself" do set_env(admin, user.id) - current_user.should == user - current_user.should == user + expect(current_user).to eq(user) + expect(current_user).to eq(user) set_env(admin, user.username) - current_user.should == user - current_user.should == user + expect(current_user).to eq(user) + expect(current_user).to eq(user) set_param(admin, user.id) - current_user.should == user - current_user.should == user + expect(current_user).to eq(user) + expect(current_user).to eq(user) set_param(admin, user.username) - current_user.should == user - current_user.should == user + expect(current_user).to eq(user) + expect(current_user).to eq(user) end it "should handle multiple sudo's to oneself using string ids" do set_env(admin, user.id.to_s) - current_user.should == user - current_user.should == user + expect(current_user).to eq(user) + expect(current_user).to eq(user) set_param(admin, user.id.to_s) - current_user.should == user - current_user.should == user + expect(current_user).to eq(user) + expect(current_user).to eq(user) end end describe '.sudo_identifier' do it "should return integers when input is an int" do set_env(admin, '123') - sudo_identifier.should == 123 + expect(sudo_identifier).to eq(123) set_env(admin, '0001234567890') - sudo_identifier.should == 1234567890 + expect(sudo_identifier).to eq(1234567890) set_param(admin, '123') - sudo_identifier.should == 123 + expect(sudo_identifier).to eq(123) set_param(admin, '0001234567890') - sudo_identifier.should == 1234567890 + expect(sudo_identifier).to eq(1234567890) end it "should return string when input is an is not an int" do set_env(admin, '12.30') - sudo_identifier.should == "12.30" + expect(sudo_identifier).to eq("12.30") set_env(admin, 'hello') - sudo_identifier.should == 'hello' + expect(sudo_identifier).to eq('hello') set_env(admin, ' 123') - sudo_identifier.should == ' 123' + expect(sudo_identifier).to eq(' 123') set_param(admin, '12.30') - sudo_identifier.should == "12.30" + expect(sudo_identifier).to eq("12.30") set_param(admin, 'hello') - sudo_identifier.should == 'hello' + expect(sudo_identifier).to eq('hello') set_param(admin, ' 123') - sudo_identifier.should == ' 123' + expect(sudo_identifier).to eq(' 123') end end end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index b45572c39fd..f40d68b75a4 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -15,79 +15,79 @@ describe API::API, api: true do describe "GET /projects/:id/repository/branches" do it "should return an array of project branches" do get api("/projects/#{project.id}/repository/branches", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['name'].should == project.repository.branch_names.first + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(project.repository.branch_names.first) end end describe "GET /projects/:id/repository/branches/:branch" do it "should return the branch information for a single branch" do get api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - response.status.should == 200 + expect(response.status).to eq(200) - json_response['name'].should == branch_name - json_response['commit']['id'].should == branch_sha - json_response['protected'].should == false + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) + expect(json_response['protected']).to eq(false) end it "should return a 403 error if guest" do get api("/projects/#{project.id}/repository/branches", user2) - response.status.should == 403 + expect(response.status).to eq(403) end it "should return a 404 error if branch is not available" do get api("/projects/#{project.id}/repository/branches/unknown", user) - response.status.should == 404 + expect(response.status).to eq(404) end end describe "PUT /projects/:id/repository/branches/:branch/protect" do it "should protect a single branch" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - response.status.should == 200 + expect(response.status).to eq(200) - json_response['name'].should == branch_name - json_response['commit']['id'].should == branch_sha - json_response['protected'].should == true + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) + expect(json_response['protected']).to eq(true) end it "should return a 404 error if branch not found" do put api("/projects/#{project.id}/repository/branches/unknown/protect", user) - response.status.should == 404 + expect(response.status).to eq(404) end it "should return a 403 error if guest" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user2) - response.status.should == 403 + expect(response.status).to eq(403) end it "should return success when protect branch again" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - response.status.should == 200 + expect(response.status).to eq(200) end end describe "PUT /projects/:id/repository/branches/:branch/unprotect" do it "should unprotect a single branch" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - response.status.should == 200 + expect(response.status).to eq(200) - json_response['name'].should == branch_name - json_response['commit']['id'].should == branch_sha - json_response['protected'].should == false + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) + expect(json_response['protected']).to eq(false) end it "should return success when unprotect branch" do put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) - response.status.should == 404 + expect(response.status).to eq(404) end it "should return success when unprotect branch again" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - response.status.should == 200 + expect(response.status).to eq(200) end end @@ -97,46 +97,46 @@ describe API::API, api: true do branch_name: 'feature1', ref: branch_sha - response.status.should == 201 + expect(response.status).to eq(201) - json_response['name'].should == 'feature1' - json_response['commit']['id'].should == branch_sha + expect(json_response['name']).to eq('feature1') + expect(json_response['commit']['id']).to eq(branch_sha) end it "should deny for user without push access" do post api("/projects/#{project.id}/repository/branches", user2), branch_name: branch_name, ref: branch_sha - response.status.should == 403 + expect(response.status).to eq(403) end it 'should return 400 if branch name is invalid' do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new design', ref: branch_sha - response.status.should == 400 - json_response['message'].should == 'Branch name invalid' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Branch name invalid') end it 'should return 400 if branch already exists' do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design1', ref: branch_sha - response.status.should == 201 + expect(response.status).to eq(201) post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design1', ref: branch_sha - response.status.should == 400 - json_response['message'].should == 'Branch already exists' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Branch already exists') end it 'should return 400 if ref name is invalid' do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design3', ref: 'foo' - response.status.should == 400 - json_response['message'].should == 'Invalid reference name' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Invalid reference name') end end @@ -145,26 +145,26 @@ describe API::API, api: true do it "should remove branch" do delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - response.status.should == 200 - json_response['branch_name'].should == branch_name + expect(response.status).to eq(200) + expect(json_response['branch_name']).to eq(branch_name) end it 'should return 404 if branch not exists' do delete api("/projects/#{project.id}/repository/branches/foobar", user) - response.status.should == 404 + expect(response.status).to eq(404) end it "should remove protected branch" do project.protected_branches.create(name: branch_name) delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - response.status.should == 405 - json_response['message'].should == 'Protected branch cant be removed' + expect(response.status).to eq(405) + expect(json_response['message']).to eq('Protected branch cant be removed') end it "should not remove HEAD branch" do delete api("/projects/#{project.id}/repository/branches/master", user) - response.status.should == 405 - json_response['message'].should == 'Cannot remove HEAD branch' + expect(response.status).to eq(405) + expect(json_response['message']).to eq('Cannot remove HEAD branch') end end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index a3f58f50913..9ea60e1a4ad 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -18,17 +18,17 @@ describe API::API, api: true do it "should return project commits" do get api("/projects/#{project.id}/repository/commits", user) - response.status.should == 200 + expect(response.status).to eq(200) - json_response.should be_an Array - json_response.first['id'].should == project.repository.commit.id + expect(json_response).to be_an Array + expect(json_response.first['id']).to eq(project.repository.commit.id) end end context "unauthorized user" do it "should not return project commits" do get api("/projects/#{project.id}/repository/commits") - response.status.should == 401 + expect(response.status).to eq(401) end end end @@ -37,21 +37,21 @@ describe API::API, api: true do context "authorized user" do it "should return a commit by sha" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) - response.status.should == 200 - json_response['id'].should == project.repository.commit.id - json_response['title'].should == project.repository.commit.title + expect(response.status).to eq(200) + expect(json_response['id']).to eq(project.repository.commit.id) + expect(json_response['title']).to eq(project.repository.commit.title) end it "should return a 404 error if not found" do get api("/projects/#{project.id}/repository/commits/invalid_sha", user) - response.status.should == 404 + expect(response.status).to eq(404) end end context "unauthorized user" do it "should not return the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}") - response.status.should == 401 + expect(response.status).to eq(401) end end end @@ -62,23 +62,23 @@ describe API::API, api: true do it "should return the diff of the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user) - response.status.should == 200 + expect(response.status).to eq(200) - json_response.should be_an Array - json_response.length.should >= 1 - json_response.first.keys.should include "diff" + expect(json_response).to be_an Array + expect(json_response.length).to be >= 1 + expect(json_response.first.keys).to include "diff" end it "should return a 404 error if invalid commit" do get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user) - response.status.should == 404 + expect(response.status).to eq(404) end end context "unauthorized user" do it "should not return the diff of the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff") - response.status.should == 401 + expect(response.status).to eq(401) end end end @@ -87,23 +87,23 @@ describe API::API, api: true do context 'authorized user' do it 'should return merge_request comments' do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['note'].should == 'a comment on a commit' - json_response.first['author']['id'].should == user.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['note']).to eq('a comment on a commit') + expect(json_response.first['author']['id']).to eq(user.id) end it 'should return a 404 error if merge_request_id not found' do get api("/projects/#{project.id}/repository/commits/1234ab/comments", user) - response.status.should == 404 + expect(response.status).to eq(404) end end context 'unauthorized user' do it 'should not return the diff of the selected commit' do get api("/projects/#{project.id}/repository/commits/1234ab/comments") - response.status.should == 401 + expect(response.status).to eq(401) end end end @@ -112,37 +112,37 @@ describe API::API, api: true do context 'authorized user' do it 'should return comment' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment' - response.status.should == 201 - json_response['note'].should == 'My comment' - json_response['path'].should be_nil - json_response['line'].should be_nil - json_response['line_type'].should be_nil + expect(response.status).to eq(201) + expect(json_response['note']).to eq('My comment') + expect(json_response['path']).to be_nil + expect(json_response['line']).to be_nil + expect(json_response['line_type']).to be_nil end it 'should return the inline comment' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.diffs.first.new_path, line: 7, line_type: 'new' - response.status.should == 201 - json_response['note'].should == 'My comment' - json_response['path'].should == project.repository.commit.diffs.first.new_path - json_response['line'].should == 7 - json_response['line_type'].should == 'new' + expect(response.status).to eq(201) + expect(json_response['note']).to eq('My comment') + expect(json_response['path']).to eq(project.repository.commit.diffs.first.new_path) + expect(json_response['line']).to eq(7) + expect(json_response['line_type']).to eq('new') end it 'should return 400 if note is missing' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return 404 if note is attached to non existent commit' do post api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment' - response.status.should == 404 + expect(response.status).to eq(404) end end context 'unauthorized user' do it 'should not return the diff of the selected commit' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments") - response.status.should == 401 + expect(response.status).to eq(401) end end end diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index ddef99d77af..39949a90422 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -11,21 +11,21 @@ describe API::API, api: true do describe "when unauthenticated" do it "returns authentication success" do get api("/user"), :access_token => token.token - response.status.should == 200 + expect(response.status).to eq(200) end end describe "when token invalid" do it "returns authentication error" do get api("/user"), :access_token => "123a" - response.status.should == 401 + expect(response.status).to eq(401) end end describe "authorization by private token" do it "returns authentication success" do get api("/user", user) - response.status.should == 200 + expect(response.status).to eq(200) end end end diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index b43a202aec0..bab8888a631 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -16,15 +16,15 @@ describe API::API, api: true do } get api("/projects/#{project.id}/repository/files", user), params - response.status.should == 200 - json_response['file_path'].should == file_path - json_response['file_name'].should == 'popen.rb' - Base64.decode64(json_response['content']).lines.first.should == "require 'fileutils'\n" + expect(response.status).to eq(200) + expect(json_response['file_path']).to eq(file_path) + expect(json_response['file_name']).to eq('popen.rb') + expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") end it "should return a 400 bad request if no params given" do get api("/projects/#{project.id}/repository/files", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 404 if such file does not exist" do @@ -34,7 +34,7 @@ describe API::API, api: true do } get api("/projects/#{project.id}/repository/files", user), params - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -54,13 +54,13 @@ describe API::API, api: true do ) post api("/projects/#{project.id}/repository/files", user), valid_params - response.status.should == 201 - json_response['file_path'].should == 'newfile.rb' + expect(response.status).to eq(201) + expect(json_response['file_path']).to eq('newfile.rb') end it "should return a 400 bad request if no params given" do post api("/projects/#{project.id}/repository/files", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 400 if satellite fails to create file" do @@ -69,7 +69,7 @@ describe API::API, api: true do ) post api("/projects/#{project.id}/repository/files", user), valid_params - response.status.should == 400 + expect(response.status).to eq(400) end end @@ -89,22 +89,42 @@ describe API::API, api: true do ) put api("/projects/#{project.id}/repository/files", user), valid_params - response.status.should == 200 - json_response['file_path'].should == file_path + expect(response.status).to eq(200) + expect(json_response['file_path']).to eq(file_path) end it "should return a 400 bad request if no params given" do put api("/projects/#{project.id}/repository/files", user) - response.status.should == 400 + expect(response.status).to eq(400) end - it "should return a 400 if satellite fails to create file" do - Gitlab::Satellite::EditFileAction.any_instance.stub( - commit!: false, - ) + it 'should return a 400 if the checkout fails' do + Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!) + .and_raise(Gitlab::Satellite::CheckoutFailed) + + put api("/projects/#{project.id}/repository/files", user), valid_params + expect(response.status).to eq(400) + + ref = valid_params[:branch_name] + expect(response.body).to match("ref '#{ref}' could not be checked out") + end + + it 'should return a 409 if the file was not modified' do + Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!) + .and_raise(Gitlab::Satellite::CommitFailed) + + put api("/projects/#{project.id}/repository/files", user), valid_params + expect(response.status).to eq(409) + expect(response.body).to match("Maybe there was nothing to commit?") + end + + it 'should return a 409 if the push fails' do + Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!) + .and_raise(Gitlab::Satellite::PushFailed) put api("/projects/#{project.id}/repository/files", user), valid_params - response.status.should == 400 + expect(response.status).to eq(409) + expect(response.body).to match("Maybe the file was changed by another process?") end end @@ -123,13 +143,13 @@ describe API::API, api: true do ) delete api("/projects/#{project.id}/repository/files", user), valid_params - response.status.should == 200 - json_response['file_path'].should == file_path + expect(response.status).to eq(200) + expect(json_response['file_path']).to eq(file_path) end it "should return a 400 bad request if no params given" do delete api("/projects/#{project.id}/repository/files", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 400 if satellite fails to create file" do @@ -138,7 +158,7 @@ describe API::API, api: true do ) delete api("/projects/#{project.id}/repository/files", user), valid_params - response.status.should == 400 + expect(response.status).to eq(400) end end end diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb index 5921b3e0698..fb3ff552c8d 100644 --- a/spec/requests/api/fork_spec.rb +++ b/spec/requests/api/fork_spec.rb @@ -23,50 +23,50 @@ describe API::API, api: true do context 'when authenticated' do it 'should fork if user has sufficient access to project' do post api("/projects/fork/#{project.id}", user2) - response.status.should == 201 - json_response['name'].should == project.name - json_response['path'].should == project.path - json_response['owner']['id'].should == user2.id - json_response['namespace']['id'].should == user2.namespace.id - json_response['forked_from_project']['id'].should == project.id + expect(response.status).to eq(201) + expect(json_response['name']).to eq(project.name) + expect(json_response['path']).to eq(project.path) + expect(json_response['owner']['id']).to eq(user2.id) + expect(json_response['namespace']['id']).to eq(user2.namespace.id) + expect(json_response['forked_from_project']['id']).to eq(project.id) end it 'should fork if user is admin' do post api("/projects/fork/#{project.id}", admin) - response.status.should == 201 - json_response['name'].should == project.name - json_response['path'].should == project.path - json_response['owner']['id'].should == admin.id - json_response['namespace']['id'].should == admin.namespace.id - json_response['forked_from_project']['id'].should == project.id + expect(response.status).to eq(201) + expect(json_response['name']).to eq(project.name) + expect(json_response['path']).to eq(project.path) + expect(json_response['owner']['id']).to eq(admin.id) + expect(json_response['namespace']['id']).to eq(admin.namespace.id) + expect(json_response['forked_from_project']['id']).to eq(project.id) end it 'should fail on missing project access for the project to fork' do post api("/projects/fork/#{project.id}", user3) - response.status.should == 404 - json_response['message'].should == '404 Project Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Project Not Found') end it 'should fail if forked project exists in the user namespace' do post api("/projects/fork/#{project.id}", user) - response.status.should == 409 - json_response['message']['base'].should == ['Invalid fork destination'] - json_response['message']['name'].should == ['has already been taken'] - json_response['message']['path'].should == ['has already been taken'] + expect(response.status).to eq(409) + expect(json_response['message']['base']).to eq(['Invalid fork destination']) + expect(json_response['message']['name']).to eq(['has already been taken']) + expect(json_response['message']['path']).to eq(['has already been taken']) end it 'should fail if project to fork from does not exist' do post api('/projects/fork/424242', user) - response.status.should == 404 - json_response['message'].should == '404 Project Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Project Not Found') end end context 'when unauthenticated' do it 'should return authentication error' do post api("/projects/fork/#{project.id}") - response.status.should == 401 - json_response['message'].should == '401 Unauthorized' + expect(response.status).to eq(401) + expect(json_response['message']).to eq('401 Unauthorized') end end end diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb index 4957186f605..8ba6876a95b 100644 --- a/spec/requests/api/group_members_spec.rb +++ b/spec/requests/api/group_members_spec.rb @@ -31,20 +31,20 @@ describe API::API, api: true do it "each user: should return an array of members groups of group3" do [owner, master, developer, reporter, guest].each do |user| get api("/groups/#{group_with_members.id}/members", user) - response.status.should == 200 - json_response.should be_an Array - json_response.size.should == 5 - json_response.find { |e| e['id']==owner.id }['access_level'].should == GroupMember::OWNER - json_response.find { |e| e['id']==reporter.id }['access_level'].should == GroupMember::REPORTER - json_response.find { |e| e['id']==developer.id }['access_level'].should == GroupMember::DEVELOPER - json_response.find { |e| e['id']==master.id }['access_level'].should == GroupMember::MASTER - json_response.find { |e| e['id']==guest.id }['access_level'].should == GroupMember::GUEST + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(5) + expect(json_response.find { |e| e['id']==owner.id }['access_level']).to eq(GroupMember::OWNER) + expect(json_response.find { |e| e['id']==reporter.id }['access_level']).to eq(GroupMember::REPORTER) + expect(json_response.find { |e| e['id']==developer.id }['access_level']).to eq(GroupMember::DEVELOPER) + expect(json_response.find { |e| e['id']==master.id }['access_level']).to eq(GroupMember::MASTER) + expect(json_response.find { |e| e['id']==guest.id }['access_level']).to eq(GroupMember::GUEST) end end it "users not part of the group should get access error" do get api("/groups/#{group_with_members.id}/members", stranger) - response.status.should == 403 + expect(response.status).to eq(403) end end end @@ -53,7 +53,7 @@ describe API::API, api: true do context "when not a member of the group" do it "should not add guest as member of group_no_members when adding being done by person outside the group" do post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER - response.status.should == 403 + expect(response.status).to eq(403) end end @@ -66,9 +66,9 @@ describe API::API, api: true do user_id: new_user.id, access_level: GroupMember::MASTER }.to change { group_no_members.members.count }.by(1) - response.status.should == 201 - json_response['name'].should == new_user.name - json_response['access_level'].should == GroupMember::MASTER + expect(response.status).to eq(201) + expect(json_response['name']).to eq(new_user.name) + expect(json_response['access_level']).to eq(GroupMember::MASTER) end it "should not allow guest to modify group members" do @@ -79,27 +79,90 @@ describe API::API, api: true do user_id: new_user.id, access_level: GroupMember::MASTER }.not_to change { group_with_members.members.count } - response.status.should == 403 + expect(response.status).to eq(403) end it "should return error if member already exists" do post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER - response.status.should == 409 + expect(response.status).to eq(409) end it "should return a 400 error when user id is not given" do post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 400 error when access level is not given" do post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 422 error when access level is not known" do post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234 - response.status.should == 422 + expect(response.status).to eq(422) + end + end + end + + describe 'PUT /groups/:id/members/:user_id' do + context 'when not a member of the group' do + it 'should return a 409 error if the user is not a group member' do + put( + api("/groups/#{group_no_members.id}/members/#{developer.id}", + owner), access_level: GroupMember::MASTER + ) + expect(response.status).to eq(404) + end + end + + context 'when a member of the group' do + it 'should return ok and update member access level' do + put( + api("/groups/#{group_with_members.id}/members/#{reporter.id}", + owner), + access_level: GroupMember::MASTER + ) + + expect(response.status).to eq(200) + + get api("/groups/#{group_with_members.id}/members", owner) + json_reporter = json_response.find do |e| + e['id'] == reporter.id + end + + expect(json_reporter['access_level']).to eq(GroupMember::MASTER) + end + + it 'should not allow guest to modify group members' do + put( + api("/groups/#{group_with_members.id}/members/#{developer.id}", + guest), + access_level: GroupMember::MASTER + ) + + expect(response.status).to eq(403) + + get api("/groups/#{group_with_members.id}/members", owner) + json_developer = json_response.find do |e| + e['id'] == developer.id + end + + expect(json_developer['access_level']).to eq(GroupMember::DEVELOPER) + end + + it 'should return a 400 error when access level is not given' do + put( + api("/groups/#{group_with_members.id}/members/#{master.id}", owner) + ) + expect(response.status).to eq(400) + end + + it 'should return a 422 error when access level is not known' do + put( + api("/groups/#{group_with_members.id}/members/#{master.id}", owner), + access_level: 1234 + ) + expect(response.status).to eq(422) end end end @@ -109,7 +172,7 @@ describe API::API, api: true do it "should not delete guest's membership of group_with_members" do random_user = create(:user) delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user) - response.status.should == 403 + expect(response.status).to eq(403) end end @@ -119,17 +182,17 @@ describe API::API, api: true do delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner) }.to change { group_with_members.members.count }.by(-1) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return a 404 error when user id is not known" do delete api("/groups/#{group_with_members.id}/members/1328", owner) - response.status.should == 404 + expect(response.status).to eq(404) end it "should not allow guest to modify group members" do delete api("/groups/#{group_with_members.id}/members/#{master.id}", guest) - response.status.should == 403 + expect(response.status).to eq(403) end end end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 8465d765294..d963dbac9f1 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -18,26 +18,26 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/groups") - response.status.should == 401 + expect(response.status).to eq(401) end end context "when authenticated as user" do it "normal user: should return an array of groups of user1" do get api("/groups", user1) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['name'].should == group1.name + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['name']).to eq(group1.name) end end context "when authenticated as admin" do it "admin: should return an array of all groups" do get api("/groups", admin) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 2 + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) end end end @@ -46,49 +46,49 @@ describe API::API, api: true do context "when authenticated as user" do it "should return one of user1's groups" do get api("/groups/#{group1.id}", user1) - response.status.should == 200 + expect(response.status).to eq(200) json_response['name'] == group1.name end it "should not return a non existing group" do get api("/groups/1328", user1) - response.status.should == 404 + expect(response.status).to eq(404) end it "should not return a group not attached to user1" do get api("/groups/#{group2.id}", user1) - response.status.should == 403 + expect(response.status).to eq(403) end end context "when authenticated as admin" do it "should return any existing group" do get api("/groups/#{group2.id}", admin) - response.status.should == 200 + expect(response.status).to eq(200) json_response['name'] == group2.name end it "should not return a non existing group" do get api("/groups/1328", admin) - response.status.should == 404 + expect(response.status).to eq(404) end end context 'when using group path in URL' do it 'should return any existing group' do get api("/groups/#{group1.path}", admin) - response.status.should == 200 + expect(response.status).to eq(200) json_response['name'] == group2.name end it 'should not return a non existing group' do get api('/groups/unknown', admin) - response.status.should == 404 + expect(response.status).to eq(404) end it 'should not return a group not attached to user1' do get api("/groups/#{group2.path}", user1) - response.status.should == 403 + expect(response.status).to eq(403) end end end @@ -97,30 +97,30 @@ describe API::API, api: true do context "when authenticated as user" do it "should not create group" do post api("/groups", user1), attributes_for(:group) - response.status.should == 403 + expect(response.status).to eq(403) end end context "when authenticated as admin" do it "should create group" do post api("/groups", admin), attributes_for(:group) - response.status.should == 201 + expect(response.status).to eq(201) end it "should not create group, duplicate" do post api("/groups", admin), {name: "Duplicate Test", path: group2.path} - response.status.should == 400 - response.message.should == "Bad Request" + expect(response.status).to eq(400) + expect(response.message).to eq("Bad Request") end it "should return 400 bad request error if name not given" do post api("/groups", admin), {path: group2.path} - response.status.should == 400 + expect(response.status).to eq(400) end it "should return 400 bad request error if path not given" do post api("/groups", admin), { name: 'test' } - response.status.should == 400 + expect(response.status).to eq(400) end end end @@ -129,36 +129,36 @@ describe API::API, api: true do context "when authenticated as user" do it "should remove group" do delete api("/groups/#{group1.id}", user1) - response.status.should == 200 + expect(response.status).to eq(200) end it "should not remove a group if not an owner" do user3 = create(:user) group1.add_user(user3, Gitlab::Access::MASTER) delete api("/groups/#{group1.id}", user3) - response.status.should == 403 + expect(response.status).to eq(403) end it "should not remove a non existing group" do delete api("/groups/1328", user1) - response.status.should == 404 + expect(response.status).to eq(404) end it "should not remove a group not attached to user1" do delete api("/groups/#{group2.id}", user1) - response.status.should == 403 + expect(response.status).to eq(403) end end context "when authenticated as admin" do it "should remove any existing group" do delete api("/groups/#{group2.id}", admin) - response.status.should == 200 + expect(response.status).to eq(200) end it "should not remove a non existing group" do delete api("/groups/1328", admin) - response.status.should == 404 + expect(response.status).to eq(404) end end end @@ -167,20 +167,20 @@ describe API::API, api: true do let(:project) { create(:project) } before(:each) do Projects::TransferService.any_instance.stub(execute: true) - Project.stub(:find).and_return(project) + allow(Project).to receive(:find).and_return(project) end context "when authenticated as user" do it "should not transfer project to group" do post api("/groups/#{group1.id}/projects/#{project.id}", user2) - response.status.should == 403 + expect(response.status).to eq(403) end end context "when authenticated as admin" do it "should transfer project to group" do post api("/groups/#{group1.id}/projects/#{project.id}", admin) - response.status.should == 201 + expect(response.status).to eq(201) end end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 1e8e9eb38d6..4c7d15d6594 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -11,8 +11,8 @@ describe API::API, api: true do it do get api("/internal/check"), secret_token: secret_token - response.status.should == 200 - json_response['api_version'].should == API::API.version + expect(response.status).to eq(200) + expect(json_response['api_version']).to eq(API::API.version) end end @@ -23,8 +23,8 @@ describe API::API, api: true do it do get api("/internal/broadcast_message"), secret_token: secret_token - response.status.should == 200 - json_response["message"].should == broadcast_message.message + expect(response.status).to eq(200) + expect(json_response["message"]).to eq(broadcast_message.message) end end @@ -32,7 +32,8 @@ describe API::API, api: true do it do get api("/internal/broadcast_message"), secret_token: secret_token - response.status.should == 404 + expect(response.status).to eq(200) + expect(json_response).to be_empty end end end @@ -41,9 +42,9 @@ describe API::API, api: true do it do get(api("/internal/discover"), key_id: key.id, secret_token: secret_token) - response.status.should == 200 + expect(response.status).to eq(200) - json_response['name'].should == user.name + expect(json_response['name']).to eq(user.name) end end @@ -57,8 +58,8 @@ describe API::API, api: true do it do pull(key, project) - response.status.should == 200 - json_response["status"].should be_true + expect(response.status).to eq(200) + expect(json_response["status"]).to be_truthy end end @@ -66,8 +67,8 @@ describe API::API, api: true do it do push(key, project) - response.status.should == 200 - json_response["status"].should be_true + expect(response.status).to eq(200) + expect(json_response["status"]).to be_truthy end end end @@ -81,8 +82,8 @@ describe API::API, api: true do it do pull(key, project) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end @@ -90,8 +91,8 @@ describe API::API, api: true do it do push(key, project) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end end @@ -107,8 +108,8 @@ describe API::API, api: true do it do pull(key, personal_project) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end @@ -116,8 +117,8 @@ describe API::API, api: true do it do push(key, personal_project) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end end @@ -134,8 +135,8 @@ describe API::API, api: true do it do pull(key, project) - response.status.should == 200 - json_response["status"].should be_true + expect(response.status).to eq(200) + expect(json_response["status"]).to be_truthy end end @@ -143,8 +144,8 @@ describe API::API, api: true do it do push(key, project) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end end @@ -160,8 +161,8 @@ describe API::API, api: true do it do archive(key, project) - response.status.should == 200 - json_response["status"].should be_true + expect(response.status).to eq(200) + expect(json_response["status"]).to be_truthy end end @@ -169,8 +170,8 @@ describe API::API, api: true do it do archive(key, project) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end end @@ -179,8 +180,8 @@ describe API::API, api: true do it do pull(key, OpenStruct.new(path_with_namespace: 'gitlab/notexists')) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end @@ -188,8 +189,8 @@ describe API::API, api: true do it do pull(OpenStruct.new(id: 0), project) - response.status.should == 200 - json_response["status"].should be_false + expect(response.status).to eq(200) + expect(json_response["status"]).to be_falsey end end end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 775d7b4e18d..b6b0427debf 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -34,86 +34,87 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/issues") - response.status.should == 401 + expect(response.status).to eq(401) end end context "when authenticated" do it "should return an array of issues" do get api("/issues", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == issue.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['title']).to eq(issue.title) end it "should add pagination headers" do get api("/issues?per_page=3", user) - response.headers['Link'].should == + expect(response.headers['Link']).to eq( '<http://www.example.com/api/v3/issues?page=1&per_page=3>; rel="first", <http://www.example.com/api/v3/issues?page=1&per_page=3>; rel="last"' + ) end it 'should return an array of closed issues' do get api('/issues?state=closed', user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['id'].should == closed_issue.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(closed_issue.id) end it 'should return an array of opened issues' do get api('/issues?state=opened', user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['id'].should == issue.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(issue.id) end it 'should return an array of all issues' do get api('/issues?state=all', user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 2 - json_response.first['id'].should == issue.id - json_response.second['id'].should == closed_issue.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + expect(json_response.first['id']).to eq(issue.id) + expect(json_response.second['id']).to eq(closed_issue.id) end it 'should return an array of labeled issues' do get api("/issues?labels=#{label.title}", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['labels'].should == [label.title] + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label.title]) end it 'should return an array of labeled issues when at least one label matches' do get api("/issues?labels=#{label.title},foo,bar", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['labels'].should == [label.title] + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label.title]) end it 'should return an empty array if no issue matches labels' do get api('/issues?labels=foo,bar', user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 0 + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) end it 'should return an array of labeled issues matching given state' do get api("/issues?labels=#{label.title}&state=opened", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['labels'].should == [label.title] - json_response.first['state'].should == 'opened' + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label.title]) + expect(json_response.first['state']).to eq('opened') end it 'should return an empty array if no issue matches labels and state filters' do get api("/issues?labels=#{label.title}&state=closed", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 0 + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) end end end @@ -124,78 +125,78 @@ describe API::API, api: true do it "should return project issues" do get api("#{base_url}/issues", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == issue.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['title']).to eq(issue.title) end it 'should return an array of labeled project issues' do get api("#{base_url}/issues?labels=#{label.title}", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['labels'].should == [label.title] + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label.title]) end it 'should return an array of labeled project issues when at least one label matches' do get api("#{base_url}/issues?labels=#{label.title},foo,bar", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['labels'].should == [label.title] + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([label.title]) end it 'should return an empty array if no project issue matches labels' do get api("#{base_url}/issues?labels=foo,bar", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 0 + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) end it 'should return an empty array if no issue matches milestone' do get api("#{base_url}/issues?milestone=#{empty_milestone.title}", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 0 + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) end it 'should return an empty array if milestone does not exist' do get api("#{base_url}/issues?milestone=foo", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 0 + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) end it 'should return an array of issues in given milestone' do get api("#{base_url}/issues?milestone=#{title}", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 2 - json_response.first['id'].should == issue.id - json_response.second['id'].should == closed_issue.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + expect(json_response.first['id']).to eq(issue.id) + expect(json_response.second['id']).to eq(closed_issue.id) end it 'should return an array of issues matching state in milestone' do get api("#{base_url}/issues?milestone=#{milestone.title}"\ '&state=closed', user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['id'].should == closed_issue.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(closed_issue.id) end end describe "GET /projects/:id/issues/:issue_id" do it "should return a project issue by id" do get api("/projects/#{project.id}/issues/#{issue.id}", user) - response.status.should == 200 - json_response['title'].should == issue.title - json_response['iid'].should == issue.iid + expect(response.status).to eq(200) + expect(json_response['title']).to eq(issue.title) + expect(json_response['iid']).to eq(issue.iid) end it "should return 404 if issue id not found" do get api("/projects/#{project.id}/issues/54321", user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -203,32 +204,32 @@ describe API::API, api: true do it "should create a new project issue" do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, label2' - response.status.should == 201 - json_response['title'].should == 'new issue' - json_response['description'].should be_nil - json_response['labels'].should == ['label', 'label2'] + expect(response.status).to eq(201) + expect(json_response['title']).to eq('new issue') + expect(json_response['description']).to be_nil + expect(json_response['labels']).to eq(['label', 'label2']) end it "should return a 400 bad request if title not given" do post api("/projects/#{project.id}/issues", user), labels: 'label, label2' - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return 400 on invalid label names' do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, ?' - response.status.should == 400 - json_response['message']['labels']['?']['title'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end it 'should return 400 if title is too long' do post api("/projects/#{project.id}/issues", user), title: 'g' * 256 - response.status.should == 400 - json_response['message']['title'].should == [ + expect(response.status).to eq(400) + expect(json_response['message']['title']).to eq([ 'is too long (maximum is 255 characters)' - ] + ]) end end @@ -236,23 +237,23 @@ describe API::API, api: true do it "should update a project issue" do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title' - response.status.should == 200 + expect(response.status).to eq(200) - json_response['title'].should == 'updated title' + expect(json_response['title']).to eq('updated title') end it "should return 404 error if issue id not found" do put api("/projects/#{project.id}/issues/44444", user), title: 'updated title' - response.status.should == 404 + expect(response.status).to eq(404) end it 'should return 400 on invalid label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title', labels: 'label, ?' - response.status.should == 400 - json_response['message']['labels']['?']['title'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end end @@ -263,49 +264,49 @@ describe API::API, api: true do it 'should not update labels if not present' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title' - response.status.should == 200 - json_response['labels'].should == [label.title] + expect(response.status).to eq(200) + expect(json_response['labels']).to eq([label.title]) end it 'should remove all labels' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: '' - response.status.should == 200 - json_response['labels'].should == [] + expect(response.status).to eq(200) + expect(json_response['labels']).to eq([]) end it 'should update labels' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'foo,bar' - response.status.should == 200 - json_response['labels'].should include 'foo' - json_response['labels'].should include 'bar' + expect(response.status).to eq(200) + expect(json_response['labels']).to include 'foo' + expect(json_response['labels']).to include 'bar' end it 'should return 400 on invalid label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label, ?' - response.status.should == 400 - json_response['message']['labels']['?']['title'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end it 'should allow special label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label:foo, label-bar,label_bar,label/bar' - response.status.should == 200 - json_response['labels'].should include 'label:foo' - json_response['labels'].should include 'label-bar' - json_response['labels'].should include 'label_bar' - json_response['labels'].should include 'label/bar' + expect(response.status).to eq(200) + expect(json_response['labels']).to include 'label:foo' + expect(json_response['labels']).to include 'label-bar' + expect(json_response['labels']).to include 'label_bar' + expect(json_response['labels']).to include 'label/bar' end it 'should return 400 if title is too long' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'g' * 256 - response.status.should == 400 - json_response['message']['title'].should == [ + expect(response.status).to eq(400) + expect(json_response['message']['title']).to eq([ 'is too long (maximum is 255 characters)' - ] + ]) end end @@ -313,17 +314,17 @@ describe API::API, api: true do it "should update a project issue" do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label2', state_event: "close" - response.status.should == 200 + expect(response.status).to eq(200) - json_response['labels'].should include 'label2' - json_response['state'].should eq "closed" + expect(json_response['labels']).to include 'label2' + expect(json_response['state']).to eq "closed" end end describe "DELETE /projects/:id/issues/:issue_id" do it "should delete a project issue" do delete api("/projects/#{project.id}/issues/#{issue.id}", user) - response.status.should == 405 + expect(response.status).to eq(405) end end end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index dbddc8a7da4..aff109a9424 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -15,10 +15,10 @@ describe API::API, api: true do describe 'GET /projects/:id/labels' do it 'should return project labels' do get api("/projects/#{project.id}/labels", user) - response.status.should == 200 - json_response.should be_an Array - json_response.size.should == 1 - json_response.first['name'].should == label1.name + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(1) + expect(json_response.first['name']).to eq(label1.name) end end @@ -27,69 +27,69 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAABB' - response.status.should == 201 - json_response['name'].should == 'Foo' - json_response['color'].should == '#FFAABB' + expect(response.status).to eq(201) + expect(json_response['name']).to eq('Foo') + expect(json_response['color']).to eq('#FFAABB') end it 'should return a 400 bad request if name not given' do post api("/projects/#{project.id}/labels", user), color: '#FFAABB' - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return a 400 bad request if color not given' do post api("/projects/#{project.id}/labels", user), name: 'Foobar' - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return 400 for invalid color' do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAA' - response.status.should == 400 - json_response['message']['color'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['color']).to eq(['is invalid']) end it 'should return 400 for too long color code' do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAAFFFF' - response.status.should == 400 - json_response['message']['color'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['color']).to eq(['is invalid']) end it 'should return 400 for invalid name' do post api("/projects/#{project.id}/labels", user), name: '?', color: '#FFAABB' - response.status.should == 400 - json_response['message']['title'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['title']).to eq(['is invalid']) end it 'should return 409 if label already exists' do post api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FFAABB' - response.status.should == 409 - json_response['message'].should == 'Label already exists' + expect(response.status).to eq(409) + expect(json_response['message']).to eq('Label already exists') end end describe 'DELETE /projects/:id/labels' do it 'should return 200 for existing label' do delete api("/projects/#{project.id}/labels", user), name: 'label1' - response.status.should == 200 + expect(response.status).to eq(200) end it 'should return 404 for non existing label' do delete api("/projects/#{project.id}/labels", user), name: 'label2' - response.status.should == 404 - json_response['message'].should == '404 Label Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Label Not Found') end it 'should return 400 for wrong parameters' do delete api("/projects/#{project.id}/labels", user) - response.status.should == 400 + expect(response.status).to eq(400) end end @@ -99,47 +99,47 @@ describe API::API, api: true do name: 'label1', new_name: 'New Label', color: '#FFFFFF' - response.status.should == 200 - json_response['name'].should == 'New Label' - json_response['color'].should == '#FFFFFF' + expect(response.status).to eq(200) + expect(json_response['name']).to eq('New Label') + expect(json_response['color']).to eq('#FFFFFF') end it 'should return 200 if name is changed' do put api("/projects/#{project.id}/labels", user), name: 'label1', new_name: 'New Label' - response.status.should == 200 - json_response['name'].should == 'New Label' - json_response['color'].should == label1.color + expect(response.status).to eq(200) + expect(json_response['name']).to eq('New Label') + expect(json_response['color']).to eq(label1.color) end it 'should return 200 if colors is changed' do put api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FFFFFF' - response.status.should == 200 - json_response['name'].should == label1.name - json_response['color'].should == '#FFFFFF' + expect(response.status).to eq(200) + expect(json_response['name']).to eq(label1.name) + expect(json_response['color']).to eq('#FFFFFF') end it 'should return 404 if label does not exist' do put api("/projects/#{project.id}/labels", user), name: 'label2', new_name: 'label3' - response.status.should == 404 + expect(response.status).to eq(404) end it 'should return 400 if no label name given' do put api("/projects/#{project.id}/labels", user), new_name: 'label2' - response.status.should == 400 - json_response['message'].should == '400 (Bad request) "name" not given' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('400 (Bad request) "name" not given') end it 'should return 400 if no new parameters given' do put api("/projects/#{project.id}/labels", user), name: 'label1' - response.status.should == 400 - json_response['message'].should == 'Required parameters '\ - '"new_name" or "color" missing' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Required parameters '\ + '"new_name" or "color" missing') end it 'should return 400 for invalid name' do @@ -147,24 +147,24 @@ describe API::API, api: true do name: 'label1', new_name: '?', color: '#FFFFFF' - response.status.should == 400 - json_response['message']['title'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['title']).to eq(['is invalid']) end it 'should return 400 for invalid name' do put api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FF' - response.status.should == 400 - json_response['message']['color'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['color']).to eq(['is invalid']) end it 'should return 400 for too long color code' do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAAFFFF' - response.status.should == 400 - json_response['message']['color'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['color']).to eq(['is invalid']) end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index b5deb072cd1..9e252441a4f 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -16,50 +16,50 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/projects/#{project.id}/merge_requests") - response.status.should == 401 + expect(response.status).to eq(401) end end context "when authenticated" do it "should return an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 3 - json_response.last['title'].should == merge_request.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + expect(json_response.last['title']).to eq(merge_request.title) end it "should return an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests?state", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 3 - json_response.last['title'].should == merge_request.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + expect(json_response.last['title']).to eq(merge_request.title) end it "should return an array of open merge_requests" do get api("/projects/#{project.id}/merge_requests?state=opened", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.last['title'].should == merge_request.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.last['title']).to eq(merge_request.title) end it "should return an array of closed merge_requests" do get api("/projects/#{project.id}/merge_requests?state=closed", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 2 - json_response.second['title'].should == merge_request_closed.title - json_response.first['title'].should == merge_request_merged.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + expect(json_response.second['title']).to eq(merge_request_closed.title) + expect(json_response.first['title']).to eq(merge_request_merged.title) end it "should return an array of merged merge_requests" do get api("/projects/#{project.id}/merge_requests?state=merged", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['title'].should == merge_request_merged.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['title']).to eq(merge_request_merged.title) end context "with ordering" do @@ -70,38 +70,38 @@ describe API::API, api: true do it "should return an array of merge_requests in ascending order" do get api("/projects/#{project.id}/merge_requests?sort=asc", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 3 - json_response.last['id'].should == @mr_earlier.id - json_response.first['id'].should == @mr_later.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + expect(json_response.last['id']).to eq(@mr_earlier.id) + expect(json_response.first['id']).to eq(@mr_later.id) end it "should return an array of merge_requests in descending order" do get api("/projects/#{project.id}/merge_requests?sort=desc", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 3 - json_response.first['id'].should == @mr_later.id - json_response.last['id'].should == @mr_earlier.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + expect(json_response.first['id']).to eq(@mr_later.id) + expect(json_response.last['id']).to eq(@mr_earlier.id) end it "should return an array of merge_requests ordered by updated_at" do get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 3 - json_response.last['id'].should == @mr_earlier.id - json_response.first['id'].should == @mr_later.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + expect(json_response.last['id']).to eq(@mr_earlier.id) + expect(json_response.first['id']).to eq(@mr_later.id) end it "should return an array of merge_requests ordered by created_at" do get api("/projects/#{project.id}/merge_requests?sort=created_at", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 3 - json_response.last['id'].should == @mr_earlier.id - json_response.first['id'].should == @mr_later.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + expect(json_response.last['id']).to eq(@mr_earlier.id) + expect(json_response.first['id']).to eq(@mr_later.id) end end end @@ -110,14 +110,14 @@ describe API::API, api: true do describe "GET /projects/:id/merge_request/:merge_request_id" do it "should return merge_request" do get api("/projects/#{project.id}/merge_request/#{merge_request.id}", user) - response.status.should == 200 - json_response['title'].should == merge_request.title - json_response['iid'].should == merge_request.iid + expect(response.status).to eq(200) + expect(json_response['title']).to eq(merge_request.title) + expect(json_response['iid']).to eq(merge_request.iid) end it "should return a 404 error if merge_request_id not found" do get api("/projects/#{project.id}/merge_request/999", user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -143,33 +143,33 @@ describe API::API, api: true do target_branch: 'master', author: user, labels: 'label, label2' - response.status.should == 201 - json_response['title'].should == 'Test merge_request' - json_response['labels'].should == ['label', 'label2'] + expect(response.status).to eq(201) + expect(json_response['title']).to eq('Test merge_request') + expect(json_response['labels']).to eq(['label', 'label2']) end it "should return 422 when source_branch equals target_branch" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", source_branch: "master", target_branch: "master", author: user - response.status.should == 422 + expect(response.status).to eq(422) end it "should return 400 when source_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", target_branch: "master", author: user - response.status.should == 400 + expect(response.status).to eq(400) end it "should return 400 when target_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", source_branch: "stable", author: user - response.status.should == 400 + expect(response.status).to eq(400) end it "should return 400 when title is missing" do post api("/projects/#{project.id}/merge_requests", user), target_branch: 'master', source_branch: 'stable' - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return 400 on invalid label names' do @@ -179,9 +179,10 @@ describe API::API, api: true do target_branch: 'master', author: user, labels: 'label, ?' - response.status.should == 400 - json_response['message']['labels']['?']['title'].should == + expect(response.status).to eq(400) + expect(json_response['message']['labels']['?']['title']).to eq( ['is invalid'] + ) end context 'with existing MR' do @@ -202,7 +203,7 @@ describe API::API, api: true do target_branch: 'master', author: user end.to change { MergeRequest.count }.by(0) - response.status.should == 409 + expect(response.status).to eq(409) end end end @@ -219,37 +220,37 @@ describe API::API, api: true do it "should return merge_request" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' - response.status.should == 201 - json_response['title'].should == 'Test merge_request' - json_response['description'].should == 'Test description for Test merge_request' + expect(response.status).to eq(201) + expect(json_response['title']).to eq('Test merge_request') + expect(json_response['description']).to eq('Test description for Test merge_request') end it "should not return 422 when source_branch equals target_branch" do - project.id.should_not == fork_project.id - fork_project.forked?.should be_true - fork_project.forked_from_project.should == project + expect(project.id).not_to eq(fork_project.id) + expect(fork_project.forked?).to be_truthy + expect(fork_project.forked_from_project).to eq(project) post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id - response.status.should == 201 - json_response['title'].should == 'Test merge_request' + expect(response.status).to eq(201) + expect(json_response['title']).to eq('Test merge_request') end it "should return 400 when source_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - response.status.should == 400 + expect(response.status).to eq(400) end it "should return 400 when target_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - response.status.should == 400 + expect(response.status).to eq(400) end it "should return 400 when title is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id - response.status.should == 400 + expect(response.status).to eq(400) end context 'when target_branch is specified' do @@ -260,7 +261,7 @@ describe API::API, api: true do source_branch: 'stable', author: user, target_project_id: fork_project.id - response.status.should == 422 + expect(response.status).to eq(422) end it 'should return 422 if targeting a different fork' do @@ -270,14 +271,14 @@ describe API::API, api: true do source_branch: 'stable', author: user2, target_project_id: unrelated_project.id - response.status.should == 422 + expect(response.status).to eq(422) end end it "should return 201 when target_branch is specified and for the same project" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id - response.status.should == 201 + expect(response.status).to eq(201) end end end @@ -285,8 +286,8 @@ describe API::API, api: true do describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do it "should return merge_request" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close" - response.status.should == 200 - json_response['state'].should == 'closed' + expect(response.status).to eq(200) + expect(json_response['state']).to eq('closed') end end @@ -294,55 +295,55 @@ describe API::API, api: true do it "should return merge_request in case of success" do MergeRequest.any_instance.stub(can_be_merged?: true, automerge!: true) put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return 405 if branch can't be merged" do MergeRequest.any_instance.stub(can_be_merged?: false) put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) - response.status.should == 405 - json_response['message'].should == 'Branch cannot be merged' + expect(response.status).to eq(405) + expect(json_response['message']).to eq('Branch cannot be merged') end it "should return 405 if merge_request is not open" do merge_request.close put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) - response.status.should == 405 - json_response['message'].should == '405 Method Not Allowed' + expect(response.status).to eq(405) + expect(json_response['message']).to eq('405 Method Not Allowed') end it "should return 401 if user has no permissions to merge" do user2 = create(:user) project.team << [user2, :reporter] put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user2) - response.status.should == 401 - json_response['message'].should == '401 Unauthorized' + expect(response.status).to eq(401) + expect(json_response['message']).to eq('401 Unauthorized') end end describe "PUT /projects/:id/merge_request/:merge_request_id" do it "should return merge_request" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title" - response.status.should == 200 - json_response['title'].should == 'New title' + expect(response.status).to eq(200) + expect(json_response['title']).to eq('New title') end it "should return merge_request" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), description: "New description" - response.status.should == 200 - json_response['description'].should == 'New description' + expect(response.status).to eq(200) + expect(json_response['description']).to eq('New description') end it "should return 422 when source_branch and target_branch are renamed the same" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), source_branch: "master", target_branch: "master" - response.status.should == 422 + expect(response.status).to eq(422) end it "should return merge_request with renamed target_branch" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "wiki" - response.status.should == 200 - json_response['target_branch'].should == 'wiki' + expect(response.status).to eq(200) + expect(json_response['target_branch']).to eq('wiki') end it 'should return 400 on invalid label names' do @@ -350,43 +351,43 @@ describe API::API, api: true do user), title: 'new issue', labels: 'label, ?' - response.status.should == 400 - json_response['message']['labels']['?']['title'].should == ['is invalid'] + expect(response.status).to eq(400) + expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end end describe "POST /projects/:id/merge_request/:merge_request_id/comments" do it "should return comment" do post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment" - response.status.should == 201 - json_response['note'].should == 'My comment' + expect(response.status).to eq(201) + expect(json_response['note']).to eq('My comment') end it "should return 400 if note is missing" do post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return 404 if note is attached to non existent merge request" do post api("/projects/#{project.id}/merge_request/404/comments", user), note: 'My comment' - response.status.should == 404 + expect(response.status).to eq(404) end end describe "GET :id/merge_request/:merge_request_id/comments" do it "should return merge_request comments" do get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user) - response.status.should == 200 - json_response.should be_an Array - json_response.length.should == 1 - json_response.first['note'].should == "a comment on a MR" - json_response.first['author']['id'].should == user.id + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['note']).to eq("a comment on a MR") + expect(json_response.first['author']['id']).to eq(user.id) end it "should return a 404 error if merge_request_id not found" do get api("/projects/#{project.id}/merge_request/999/comments", user) - response.status.should == 404 + expect(response.status).to eq(404) end end diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index 647033309bd..effb0723476 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -11,55 +11,55 @@ describe API::API, api: true do describe 'GET /projects/:id/milestones' do it 'should return project milestones' do get api("/projects/#{project.id}/milestones", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == milestone.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['title']).to eq(milestone.title) end it 'should return a 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones") - response.status.should == 401 + expect(response.status).to eq(401) end end describe 'GET /projects/:id/milestones/:milestone_id' do it 'should return a project milestone by id' do get api("/projects/#{project.id}/milestones/#{milestone.id}", user) - response.status.should == 200 - json_response['title'].should == milestone.title - json_response['iid'].should == milestone.iid + expect(response.status).to eq(200) + expect(json_response['title']).to eq(milestone.title) + expect(json_response['iid']).to eq(milestone.iid) end it 'should return 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones/#{milestone.id}") - response.status.should == 401 + expect(response.status).to eq(401) end it 'should return a 404 error if milestone id not found' do get api("/projects/#{project.id}/milestones/1234", user) - response.status.should == 404 + expect(response.status).to eq(404) end end describe 'POST /projects/:id/milestones' do it 'should create a new project milestone' do post api("/projects/#{project.id}/milestones", user), title: 'new milestone' - response.status.should == 201 - json_response['title'].should == 'new milestone' - json_response['description'].should be_nil + expect(response.status).to eq(201) + expect(json_response['title']).to eq('new milestone') + expect(json_response['description']).to be_nil end it 'should create a new project milestone with description and due date' do post api("/projects/#{project.id}/milestones", user), title: 'new milestone', description: 'release', due_date: '2013-03-02' - response.status.should == 201 - json_response['description'].should == 'release' - json_response['due_date'].should == '2013-03-02' + expect(response.status).to eq(201) + expect(json_response['description']).to eq('release') + expect(json_response['due_date']).to eq('2013-03-02') end it 'should return a 400 error if title is missing' do post api("/projects/#{project.id}/milestones", user) - response.status.should == 400 + expect(response.status).to eq(400) end end @@ -67,14 +67,14 @@ describe API::API, api: true do it 'should update a project milestone' do put api("/projects/#{project.id}/milestones/#{milestone.id}", user), title: 'updated title' - response.status.should == 200 - json_response['title'].should == 'updated title' + expect(response.status).to eq(200) + expect(json_response['title']).to eq('updated title') end it 'should return a 404 error if milestone id not found' do put api("/projects/#{project.id}/milestones/1234", user), title: 'updated title' - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -82,15 +82,15 @@ describe API::API, api: true do it 'should update a project milestone' do put api("/projects/#{project.id}/milestones/#{milestone.id}", user), state_event: 'close' - response.status.should == 200 + expect(response.status).to eq(200) - json_response['state'].should == 'closed' + expect(json_response['state']).to eq('closed') end end describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do it 'should create an activity event when an milestone is closed' do - Event.should_receive(:create) + expect(Event).to receive(:create) put api("/projects/#{project.id}/milestones/#{milestone.id}", user), state_event: 'close' @@ -103,14 +103,14 @@ describe API::API, api: true do end it 'should return project issues for a particular milestone' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['milestone']['title'].should == milestone.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['milestone']['title']).to eq(milestone.title) end it 'should return a 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues") - response.status.should == 401 + expect(response.status).to eq(401) end end end diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb index b8943ea0762..6ddaaa0a6dd 100644 --- a/spec/requests/api/namespaces_spec.rb +++ b/spec/requests/api/namespaces_spec.rb @@ -10,17 +10,17 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/namespaces") - response.status.should == 401 + expect(response.status).to eq(401) end end context "when authenticated as admin" do it "admin: should return an array of all namespaces" do get api("/namespaces", admin) - response.status.should == 200 - json_response.should be_an Array + expect(response.status).to eq(200) + expect(json_response).to be_an Array - json_response.length.should == Namespace.count + expect(json_response.length).to eq(Namespace.count) end end end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index 429824e829a..8b177af4689 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -16,42 +16,42 @@ describe API::API, api: true do context "when noteable is an Issue" do it "should return an array of issue notes" do get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['body'].should == issue_note.note + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(issue_note.note) end it "should return a 404 error when issue id not found" do get api("/projects/#{project.id}/issues/123/notes", user) - response.status.should == 404 + expect(response.status).to eq(404) end end context "when noteable is a Snippet" do it "should return an array of snippet notes" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['body'].should == snippet_note.note + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(snippet_note.note) end it "should return a 404 error when snippet id not found" do get api("/projects/#{project.id}/snippets/42/notes", user) - response.status.should == 404 + expect(response.status).to eq(404) end end context "when noteable is a Merge Request" do it "should return an array of merge_requests notes" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['body'].should == merge_request_note.note + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(merge_request_note.note) end it "should return a 404 error if merge request id not found" do get api("/projects/#{project.id}/merge_requests/4444/notes", user) - response.status.should == 404 + expect(response.status).to eq(404) end end end @@ -60,26 +60,26 @@ describe API::API, api: true do context "when noteable is an Issue" do it "should return an issue note by id" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user) - response.status.should == 200 - json_response['body'].should == issue_note.note + expect(response.status).to eq(200) + expect(json_response['body']).to eq(issue_note.note) end it "should return a 404 error if issue note not found" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user) - response.status.should == 404 + expect(response.status).to eq(404) end end context "when noteable is a Snippet" do it "should return a snippet note by id" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) - response.status.should == 200 - json_response['body'].should == snippet_note.note + expect(response.status).to eq(200) + expect(json_response['body']).to eq(snippet_note.note) end it "should return a 404 error if snippet note not found" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/123", user) - response.status.should == 404 + expect(response.status).to eq(404) end end end @@ -88,45 +88,45 @@ describe API::API, api: true do context "when noteable is an Issue" do it "should create a new issue note" do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' - response.status.should == 201 - json_response['body'].should == 'hi!' - json_response['author']['username'].should == user.username + expect(response.status).to eq(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) end it "should return a 400 bad request error if body not given" do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 401 unauthorized error if user not authenticated" do post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' - response.status.should == 401 + expect(response.status).to eq(401) end end context "when noteable is a Snippet" do it "should create a new snippet note" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!' - response.status.should == 201 - json_response['body'].should == 'hi!' - json_response['author']['username'].should == user.username + expect(response.status).to eq(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) end it "should return a 400 bad request error if body not given" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 401 unauthorized error if user not authenticated" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' - response.status.should == 401 + expect(response.status).to eq(401) end end end describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do it "should create an activity event when an issue note is created" do - Event.should_receive(:create) + expect(Event).to receive(:create) post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' end @@ -137,20 +137,20 @@ describe API::API, api: true do it 'should return modified note' do put api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user), body: 'Hello!' - response.status.should == 200 - json_response['body'].should == 'Hello!' + expect(response.status).to eq(200) + expect(json_response['body']).to eq('Hello!') end it 'should return a 404 error when note id not found' do put api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user), body: 'Hello!' - response.status.should == 404 + expect(response.status).to eq(404) end it 'should return a 400 bad request error if body not given' do put api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) - response.status.should == 400 + expect(response.status).to eq(400) end end @@ -158,14 +158,14 @@ describe API::API, api: true do it 'should return modified note' do put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user), body: 'Hello!' - response.status.should == 200 - json_response['body'].should == 'Hello!' + expect(response.status).to eq(200) + expect(json_response['body']).to eq('Hello!') end it 'should return a 404 error when note id not found' do put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/123", user), body: "Hello!" - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -173,14 +173,14 @@ describe API::API, api: true do it 'should return modified note' do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ "notes/#{merge_request_note.id}", user), body: 'Hello!' - response.status.should == 200 - json_response['body'].should == 'Hello!' + expect(response.status).to eq(200) + expect(json_response['body']).to eq('Hello!') end it 'should return a 404 error when note id not found' do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ "notes/123", user), body: "Hello!" - response.status.should == 404 + expect(response.status).to eq(404) end end end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index cdb5e3d0612..81fe68de662 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -16,18 +16,18 @@ describe API::API, 'ProjectHooks', api: true do context "authorized user" do it "should return project hooks" do get api("/projects/#{project.id}/hooks", user) - response.status.should == 200 + expect(response.status).to eq(200) - json_response.should be_an Array - json_response.count.should == 1 - json_response.first['url'].should == "http://example.com" + expect(json_response).to be_an Array + expect(json_response.count).to eq(1) + expect(json_response.first['url']).to eq("http://example.com") end end context "unauthorized user" do it "should not access project hooks" do get api("/projects/#{project.id}/hooks", user3) - response.status.should == 403 + expect(response.status).to eq(403) end end end @@ -36,26 +36,26 @@ describe API::API, 'ProjectHooks', api: true do context "authorized user" do it "should return a project hook" do get api("/projects/#{project.id}/hooks/#{hook.id}", user) - response.status.should == 200 - json_response['url'].should == hook.url + expect(response.status).to eq(200) + expect(json_response['url']).to eq(hook.url) end it "should return a 404 error if hook id is not available" do get api("/projects/#{project.id}/hooks/1234", user) - response.status.should == 404 + expect(response.status).to eq(404) end end context "unauthorized user" do it "should not access an existing hook" do get api("/projects/#{project.id}/hooks/#{hook.id}", user3) - response.status.should == 403 + expect(response.status).to eq(403) end end it "should return a 404 error if hook id is not available" do get api("/projects/#{project.id}/hooks/1234", user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -65,17 +65,17 @@ describe API::API, 'ProjectHooks', api: true do post api("/projects/#{project.id}/hooks", user), url: "http://example.com", issues_events: true }.to change {project.hooks.count}.by(1) - response.status.should == 201 + expect(response.status).to eq(201) end it "should return a 400 error if url not given" do post api("/projects/#{project.id}/hooks", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 422 error if url not valid" do post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" - response.status.should == 422 + expect(response.status).to eq(422) end end @@ -83,23 +83,23 @@ describe API::API, 'ProjectHooks', api: true do it "should update an existing project hook" do put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'http://example.org', push_events: false - response.status.should == 200 - json_response['url'].should == 'http://example.org' + expect(response.status).to eq(200) + expect(json_response['url']).to eq('http://example.org') end it "should return 404 error if hook id not found" do put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' - response.status.should == 404 + expect(response.status).to eq(404) end it "should return 400 error if url is not given" do put api("/projects/#{project.id}/hooks/#{hook.id}", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 422 error if url is not valid" do put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' - response.status.should == 422 + expect(response.status).to eq(422) end end @@ -108,22 +108,22 @@ describe API::API, 'ProjectHooks', api: true do expect { delete api("/projects/#{project.id}/hooks/#{hook.id}", user) }.to change {project.hooks.count}.by(-1) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return success when deleting hook" do delete api("/projects/#{project.id}/hooks/#{hook.id}", user) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return success when deleting non existent hook" do delete api("/projects/#{project.id}/hooks/42", user) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return a 405 error if hook id not given" do delete api("/projects/#{project.id}/hooks", user) - response.status.should == 405 + expect(response.status).to eq(405) end end end diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb index 836f21f3e0b..8419a364ed1 100644 --- a/spec/requests/api/project_members_spec.rb +++ b/spec/requests/api/project_members_spec.rb @@ -15,23 +15,23 @@ describe API::API, api: true do it "should return project team members" do get api("/projects/#{project.id}/members", user) - response.status.should == 200 - json_response.should be_an Array - json_response.count.should == 2 - json_response.map { |u| u['username'] }.should include user.username + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.count).to eq(2) + expect(json_response.map { |u| u['username'] }).to include user.username end it "finds team members with query string" do get api("/projects/#{project.id}/members", user), query: user.username - response.status.should == 200 - json_response.should be_an Array - json_response.count.should == 1 - json_response.first['username'].should == user.username + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.count).to eq(1) + expect(json_response.first['username']).to eq(user.username) end it "should return a 404 error if id not found" do get api("/projects/9999/members", user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -40,14 +40,14 @@ describe API::API, api: true do it "should return project team member" do get api("/projects/#{project.id}/members/#{user.id}", user) - response.status.should == 200 - json_response['username'].should == user.username - json_response['access_level'].should == ProjectMember::MASTER + expect(response.status).to eq(200) + expect(json_response['username']).to eq(user.username) + expect(json_response['access_level']).to eq(ProjectMember::MASTER) end it "should return a 404 error if user id not found" do get api("/projects/#{project.id}/members/1234", user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -58,9 +58,9 @@ describe API::API, api: true do access_level: ProjectMember::DEVELOPER }.to change { ProjectMember.count }.by(1) - response.status.should == 201 - json_response['username'].should == user2.username - json_response['access_level'].should == ProjectMember::DEVELOPER + expect(response.status).to eq(201) + expect(json_response['username']).to eq(user2.username) + expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER) end it "should return a 201 status if user is already project member" do @@ -69,26 +69,26 @@ describe API::API, api: true do expect { post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: ProjectMember::DEVELOPER - }.not_to change { ProjectMember.count }.by(1) + }.not_to change { ProjectMember.count } - response.status.should == 201 - json_response['username'].should == user2.username - json_response['access_level'].should == ProjectMember::DEVELOPER + expect(response.status).to eq(201) + expect(json_response['username']).to eq(user2.username) + expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER) end it "should return a 400 error when user id is not given" do post api("/projects/#{project.id}/members", user), access_level: ProjectMember::MASTER - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 400 error when access level is not given" do post api("/projects/#{project.id}/members", user), user_id: user2.id - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 422 error when access level is not known" do post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234 - response.status.should == 422 + expect(response.status).to eq(422) end end @@ -97,24 +97,24 @@ describe API::API, api: true do it "should update project team member" do put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: ProjectMember::MASTER - response.status.should == 200 - json_response['username'].should == user3.username - json_response['access_level'].should == ProjectMember::MASTER + expect(response.status).to eq(200) + expect(json_response['username']).to eq(user3.username) + expect(json_response['access_level']).to eq(ProjectMember::MASTER) end it "should return a 404 error if user_id is not found" do put api("/projects/#{project.id}/members/1234", user), access_level: ProjectMember::MASTER - response.status.should == 404 + expect(response.status).to eq(404) end it "should return a 400 error when access level is not given" do put api("/projects/#{project.id}/members/#{user3.id}", user) - response.status.should == 400 + expect(response.status).to eq(400) end it "should return a 422 error when access level is not known" do put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123 - response.status.should == 422 + expect(response.status).to eq(422) end end @@ -132,22 +132,22 @@ describe API::API, api: true do delete api("/projects/#{project.id}/members/#{user3.id}", user) expect { delete api("/projects/#{project.id}/members/#{user3.id}", user) - }.to_not change { ProjectMember.count }.by(1) + }.to_not change { ProjectMember.count } end it "should return 200 if team member already removed" do delete api("/projects/#{project.id}/members/#{user3.id}", user) delete api("/projects/#{project.id}/members/#{user3.id}", user) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return 200 OK when the user was not member" do expect { delete api("/projects/#{project.id}/members/1000000", user) }.to change { ProjectMember.count }.by(0) - response.status.should == 200 - json_response['message'].should == "Access revoked" - json_response['id'].should == 1000000 + expect(response.status).to eq(200) + expect(json_response['message']).to eq("Access revoked") + expect(json_response['id']).to eq(1000000) end end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index dc410107410..0b3a47e3273 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -44,25 +44,25 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api('/projects') - response.status.should == 401 + expect(response.status).to eq(401) end end context 'when authenticated' do it 'should return an array of projects' do get api('/projects', user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['name'].should == project.name - json_response.first['owner']['username'].should == user.username + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(project.name) + expect(json_response.first['owner']['username']).to eq(user.username) end context 'and using search' do it 'should return searched project' do get api('/projects', user), { search: project.name } - response.status.should eq(200) - json_response.should be_an Array - json_response.length.should eq(1) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) end end @@ -74,9 +74,9 @@ describe API::API, api: true do it 'should return the correct order when sorted by id' do get api('/projects', user), { order_by: 'id', sort: 'desc'} - response.status.should eq(200) - json_response.should be_an Array - json_response.first['id'].should eq(project3.id) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['id']).to eq(project3.id) end end end @@ -88,88 +88,64 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api('/projects/all') - response.status.should == 401 + expect(response.status).to eq(401) end end context 'when authenticated as regular user' do it 'should return authentication error' do get api('/projects/all', user) - response.status.should == 403 + expect(response.status).to eq(403) end end context 'when authenticated as admin' do it 'should return an array of all projects' do get api('/projects/all', admin) - response.status.should == 200 - json_response.should be_an Array + expect(response.status).to eq(200) + expect(json_response).to be_an Array project_name = project.name - json_response.detect { + expect(json_response.detect { |project| project['name'] == project_name - }['name'].should == project_name + }['name']).to eq(project_name) - json_response.detect { + expect(json_response.detect { |project| project['owner']['username'] == user.username - }['owner']['username'].should == user.username + }['owner']['username']).to eq(user.username) end end end describe 'POST /projects' do context 'maximum number of projects reached' do - before do - (1..user2.projects_limit).each do |project| - post api('/projects', user2), name: "foo#{project}" - end - end - - it 'should not create new project' do + it 'should not create new project and respond with 403' do + allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0) expect { post api('/projects', user2), name: 'foo' }.to change {Project.count}.by(0) + expect(response.status).to eq(403) end end - it 'should create new project without path' do - expect { post api('/projects', user), name: 'foo' }.to change {Project.count}.by(1) - end - - it 'should not create new project without name' do - expect { post api('/projects', user) }.to_not change {Project.count} - end - - it 'should return a 400 error if name not given' do - post api('/projects', user) - response.status.should == 400 + it 'should create new project without path and return 201' do + expect { post api('/projects', user), name: 'foo' }. + to change { Project.count }.by(1) + expect(response.status).to eq(201) end it 'should create last project before reaching project limit' do - (1..user2.projects_limit-1).each { |p| post api('/projects', user2), name: "foo#{p}" } + allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1) post api('/projects', user2), name: 'foo' - response.status.should == 201 - end - - it 'should respond with 201 on success' do - post api('/projects', user), name: 'foo' - response.status.should == 201 - end - - it 'should respond with 400 if name is not given' do - post api('/projects', user) - response.status.should == 400 + expect(response.status).to eq(201) end - it 'should return a 403 error if project limit reached' do - (1..user.projects_limit).each do |p| - post api('/projects', user), name: "foo#{p}" - end - post api('/projects', user), name: 'bar' - response.status.should == 403 + it 'should not create new project without name and return 400' do + expect { post api('/projects', user) }.to_not change { Project.count } + expect(response.status).to eq(400) end - it 'should assign attributes to project' do + it "should assign attributes to project" do project = attributes_for(:project, { path: 'camelCasePath', description: Faker::Lorem.sentence, @@ -181,50 +157,50 @@ describe API::API, api: true do post api('/projects', user), project project.each_pair do |k,v| - json_response[k.to_s].should == v + expect(json_response[k.to_s]).to eq(v) end end it 'should set a project as public' do project = attributes_for(:project, :public) post api('/projects', user), project - json_response['public'].should be_true - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC + expect(json_response['public']).to be_truthy + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC) end it 'should set a project as public using :public' do project = attributes_for(:project, { public: true }) post api('/projects', user), project - json_response['public'].should be_true - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC + expect(json_response['public']).to be_truthy + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC) end it 'should set a project as internal' do project = attributes_for(:project, :internal) post api('/projects', user), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL) end it 'should set a project as internal overriding :public' do project = attributes_for(:project, :internal, { public: true }) post api('/projects', user), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL) end it 'should set a project as private' do project = attributes_for(:project, :private) post api('/projects', user), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE) end it 'should set a project as private using :public' do project = attributes_for(:project, { public: false }) post api('/projects', user), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE) end end @@ -232,32 +208,26 @@ describe API::API, api: true do before { project } before { admin } - it 'should create new project without path' do + it 'should create new project without path and return 201' do expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1) + expect(response.status).to eq(201) end - it 'should not create new project without name' do - expect { post api("/projects/user/#{user.id}", admin) }.to_not change {Project.count} - end - - it 'should respond with 201 on success' do - post api("/projects/user/#{user.id}", admin), name: 'foo' - response.status.should == 201 - end + it 'should respond with 400 on failure and not project' do + expect { post api("/projects/user/#{user.id}", admin) }. + to_not change { Project.count } - it 'should respond with 400 on failure' do - post api("/projects/user/#{user.id}", admin) - response.status.should == 400 - json_response['message']['name'].should == [ + expect(response.status).to eq(400) + expect(json_response['message']['name']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)', Gitlab::Regex.project_regex_message - ] - json_response['message']['path'].should == [ + ]) + expect(json_response['message']['path']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)', Gitlab::Regex.send(:default_regex_message) - ] + ]) end it 'should assign attributes to project' do @@ -272,50 +242,50 @@ describe API::API, api: true do project.each_pair do |k,v| next if k == :path - json_response[k.to_s].should == v + expect(json_response[k.to_s]).to eq(v) end end it 'should set a project as public' do project = attributes_for(:project, :public) post api("/projects/user/#{user.id}", admin), project - json_response['public'].should be_true - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC + expect(json_response['public']).to be_truthy + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC) end it 'should set a project as public using :public' do project = attributes_for(:project, { public: true }) post api("/projects/user/#{user.id}", admin), project - json_response['public'].should be_true - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC + expect(json_response['public']).to be_truthy + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC) end it 'should set a project as internal' do project = attributes_for(:project, :internal) post api("/projects/user/#{user.id}", admin), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL) end it 'should set a project as internal overriding :public' do project = attributes_for(:project, :internal, { public: true }) post api("/projects/user/#{user.id}", admin), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL) end it 'should set a project as private' do project = attributes_for(:project, :private) post api("/projects/user/#{user.id}", admin), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE) end it 'should set a project as private using :public' do project = attributes_for(:project, { public: false }) post api("/projects/user/#{user.id}", admin), project - json_response['public'].should be_false - json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE + expect(json_response['public']).to be_falsey + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE) end end @@ -325,78 +295,80 @@ describe API::API, api: true do it 'should return a project by id' do get api("/projects/#{project.id}", user) - response.status.should == 200 - json_response['name'].should == project.name - json_response['owner']['username'].should == user.username + expect(response.status).to eq(200) + expect(json_response['name']).to eq(project.name) + expect(json_response['owner']['username']).to eq(user.username) end it 'should return a project by path name' do get api("/projects/#{project.id}", user) - response.status.should == 200 - json_response['name'].should == project.name + expect(response.status).to eq(200) + expect(json_response['name']).to eq(project.name) end it 'should return a 404 error if not found' do get api('/projects/42', user) - response.status.should == 404 - json_response['message'].should == '404 Project Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Project Not Found') end it 'should return a 404 error if user is not a member' do other_user = create(:user) get api("/projects/#{project.id}", other_user) - response.status.should == 404 + expect(response.status).to eq(404) end describe 'permissions' do context 'personal project' do - before do + it 'Sets project access and returns 200' do project.team << [user, :master] get api("/projects/#{project.id}", user) - end - it { response.status.should == 200 } - it { json_response['permissions']['project_access']['access_level'].should == Gitlab::Access::MASTER } - it { json_response['permissions']['group_access'].should be_nil } + expect(response.status).to eq(200) + expect(json_response['permissions']['project_access']['access_level']). + to eq(Gitlab::Access::MASTER) + expect(json_response['permissions']['group_access']).to be_nil + end end context 'group project' do - before do + it 'should set the owner and return 200' do project2 = create(:project, group: create(:group)) project2.group.add_owner(user) get api("/projects/#{project2.id}", user) - end - it { response.status.should == 200 } - it { json_response['permissions']['project_access'].should be_nil } - it { json_response['permissions']['group_access']['access_level'].should == Gitlab::Access::OWNER } + expect(response.status).to eq(200) + expect(json_response['permissions']['project_access']).to be_nil + expect(json_response['permissions']['group_access']['access_level']). + to eq(Gitlab::Access::OWNER) + end end end end describe 'GET /projects/:id/events' do - before { project_member } + before { project_member2 } it 'should return a project events' do get api("/projects/#{project.id}/events", user) - response.status.should == 200 + expect(response.status).to eq(200) json_event = json_response.first - json_event['action_name'].should == 'joined' - json_event['project_id'].to_i.should == project.id - json_event['author_username'].should == user.username + expect(json_event['action_name']).to eq('joined') + expect(json_event['project_id'].to_i).to eq(project.id) + expect(json_event['author_username']).to eq(user3.username) end it 'should return a 404 error if not found' do get api('/projects/42/events', user) - response.status.should == 404 - json_response['message'].should == '404 Project Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Project Not Found') end it 'should return a 404 error if user is not a member' do other_user = create(:user) get api("/projects/#{project.id}/events", other_user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -405,22 +377,22 @@ describe API::API, api: true do it 'should return an array of project snippets' do get api("/projects/#{project.id}/snippets", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == snippet.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['title']).to eq(snippet.title) end end describe 'GET /projects/:id/snippets/:snippet_id' do it 'should return a project snippet' do get api("/projects/#{project.id}/snippets/#{snippet.id}", user) - response.status.should == 200 - json_response['title'].should == snippet.title + expect(response.status).to eq(200) + expect(json_response['title']).to eq(snippet.title) end it 'should return a 404 error if snippet id not found' do get api("/projects/#{project.id}/snippets/1234", user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -428,26 +400,13 @@ describe API::API, api: true do it 'should create a new project snippet' do post api("/projects/#{project.id}/snippets", user), title: 'api test', file_name: 'sample.rb', code: 'test' - response.status.should == 201 - json_response['title'].should == 'api test' + expect(response.status).to eq(201) + expect(json_response['title']).to eq('api test') end - it 'should return a 400 error if title is not given' do - post api("/projects/#{project.id}/snippets", user), - file_name: 'sample.rb', code: 'test' - response.status.should == 400 - end - - it 'should return a 400 error if file_name not given' do - post api("/projects/#{project.id}/snippets", user), - title: 'api test', code: 'test' - response.status.should == 400 - end - - it 'should return a 400 error if code not given' do - post api("/projects/#{project.id}/snippets", user), - title: 'api test', file_name: 'sample.rb' - response.status.should == 400 + it 'should return a 400 error if invalid snippet is given' do + post api("/projects/#{project.id}/snippets", user) + expect(status).to eq(400) end end @@ -455,16 +414,16 @@ describe API::API, api: true do it 'should update an existing project snippet' do put api("/projects/#{project.id}/snippets/#{snippet.id}", user), code: 'updated code' - response.status.should == 200 - json_response['title'].should == 'example' - snippet.reload.content.should == 'updated code' + expect(response.status).to eq(200) + expect(json_response['title']).to eq('example') + expect(snippet.reload.content).to eq('updated code') end it 'should update an existing project snippet with new title' do put api("/projects/#{project.id}/snippets/#{snippet.id}", user), title: 'other api test' - response.status.should == 200 - json_response['title'].should == 'other api test' + expect(response.status).to eq(200) + expect(json_response['title']).to eq('other api test') end end @@ -475,24 +434,24 @@ describe API::API, api: true do expect { delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) }.to change { Snippet.count }.by(-1) - response.status.should == 200 + expect(response.status).to eq(200) end it 'should return 404 when deleting unknown snippet id' do delete api("/projects/#{project.id}/snippets/1234", user) - response.status.should == 404 + expect(response.status).to eq(404) end end describe 'GET /projects/:id/snippets/:snippet_id/raw' do it 'should get a raw project snippet' do get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) - response.status.should == 200 + expect(response.status).to eq(200) end it 'should return a 404 error if raw project snippet not found' do get api("/projects/#{project.id}/snippets/5555/raw", user) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -505,43 +464,43 @@ describe API::API, api: true do it 'should return array of ssh keys' do get api("/projects/#{project.id}/keys", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == deploy_key.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['title']).to eq(deploy_key.title) end end describe 'GET /projects/:id/keys/:key_id' do it 'should return a single key' do get api("/projects/#{project.id}/keys/#{deploy_key.id}", user) - response.status.should == 200 - json_response['title'].should == deploy_key.title + expect(response.status).to eq(200) + expect(json_response['title']).to eq(deploy_key.title) end it 'should return 404 Not Found with invalid ID' do get api("/projects/#{project.id}/keys/404", user) - response.status.should == 404 + expect(response.status).to eq(404) end end describe 'POST /projects/:id/keys' do it 'should not create an invalid ssh key' do post api("/projects/#{project.id}/keys", user), { title: 'invalid key' } - response.status.should == 400 - json_response['message']['key'].should == [ + expect(response.status).to eq(400) + expect(json_response['message']['key']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)', 'is invalid' - ] + ]) end it 'should not create a key without title' do post api("/projects/#{project.id}/keys", user), key: 'some key' - response.status.should == 400 - json_response['message']['title'].should == [ + expect(response.status).to eq(400) + expect(json_response['message']['title']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)' - ] + ]) end it 'should create new ssh key' do @@ -563,7 +522,7 @@ describe API::API, api: true do it 'should return 404 Not Found with invalid ID' do delete api("/projects/#{project.id}/keys/404", user) - response.status.should == 404 + expect(response.status).to eq(404) end end end @@ -577,33 +536,33 @@ describe API::API, api: true do it "shouldn't available for non admin users" do post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) - response.status.should == 403 + expect(response.status).to eq(403) end it 'should allow project to be forked from an existing project' do - project_fork_target.forked?.should_not be_true + expect(project_fork_target.forked?).not_to be_truthy post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) - response.status.should == 201 + expect(response.status).to eq(201) project_fork_target.reload - project_fork_target.forked_from_project.id.should == project_fork_source.id - project_fork_target.forked_project_link.should_not be_nil - project_fork_target.forked?.should be_true + expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) + expect(project_fork_target.forked_project_link).not_to be_nil + expect(project_fork_target.forked?).to be_truthy end it 'should fail if forked_from project which does not exist' do post api("/projects/#{project_fork_target.id}/fork/9999", admin) - response.status.should == 404 + expect(response.status).to eq(404) end it 'should fail with 409 if already forked' do post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) project_fork_target.reload - project_fork_target.forked_from_project.id.should == project_fork_source.id + expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin) - response.status.should == 409 + expect(response.status).to eq(409) project_fork_target.reload - project_fork_target.forked_from_project.id.should == project_fork_source.id - project_fork_target.forked?.should be_true + expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) + expect(project_fork_target.forked?).to be_truthy end end @@ -611,26 +570,26 @@ describe API::API, api: true do it "shouldn't available for non admin users" do delete api("/projects/#{project_fork_target.id}/fork", user) - response.status.should == 403 + expect(response.status).to eq(403) end it 'should make forked project unforked' do post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) project_fork_target.reload - project_fork_target.forked_from_project.should_not be_nil - project_fork_target.forked?.should be_true + expect(project_fork_target.forked_from_project).not_to be_nil + expect(project_fork_target.forked?).to be_truthy delete api("/projects/#{project_fork_target.id}/fork", admin) - response.status.should == 200 + expect(response.status).to eq(200) project_fork_target.reload - project_fork_target.forked_from_project.should be_nil - project_fork_target.forked?.should_not be_true + expect(project_fork_target.forked_from_project).to be_nil + expect(project_fork_target.forked?).not_to be_truthy end it 'should be idempotent if not forked' do - project_fork_target.forked_from_project.should be_nil + expect(project_fork_target.forked_from_project).to be_nil delete api("/projects/#{project_fork_target.id}/fork", admin) - response.status.should == 200 - project_fork_target.reload.forked_from_project.should be_nil + expect(response.status).to eq(200) + expect(project_fork_target.reload.forked_from_project).to be_nil end end end @@ -650,27 +609,27 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/projects/search/#{query}") - response.status.should == 401 + expect(response.status).to eq(401) end end context 'when authenticated' do it 'should return an array of projects' do get api("/projects/search/#{query}",user) - response.status.should == 200 - json_response.should be_an Array - json_response.size.should == 6 - json_response.each {|project| project['name'].should =~ /.*query.*/} + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(6) + json_response.each {|project| expect(project['name']).to match(/.*query.*/)} end end context 'when authenticated as a different user' do it 'should return matching public projects' do get api("/projects/search/#{query}", user2) - response.status.should == 200 - json_response.should be_an Array - json_response.size.should == 2 - json_response.each {|project| project['name'].should =~ /(internal|public) query/} + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(2) + json_response.each {|project| expect(project['name']).to match(/(internal|public) query/)} end end end @@ -689,7 +648,7 @@ describe API::API, api: true do it 'should return authentication error' do project_param = { name: 'bar' } put api("/projects/#{project.id}"), project_param - response.status.should == 401 + expect(response.status).to eq(401) end end @@ -697,34 +656,34 @@ describe API::API, api: true do it 'should update name' do project_param = { name: 'bar' } put api("/projects/#{project.id}", user), project_param - response.status.should == 200 + expect(response.status).to eq(200) project_param.each_pair do |k, v| - json_response[k.to_s].should == v + expect(json_response[k.to_s]).to eq(v) end end it 'should update visibility_level' do project_param = { visibility_level: 20 } put api("/projects/#{project3.id}", user), project_param - response.status.should == 200 + expect(response.status).to eq(200) project_param.each_pair do |k, v| - json_response[k.to_s].should == v + expect(json_response[k.to_s]).to eq(v) end end it 'should not update name to existing name' do project_param = { name: project3.name } put api("/projects/#{project.id}", user), project_param - response.status.should == 400 - json_response['message']['name'].should == ['has already been taken'] + expect(response.status).to eq(400) + expect(json_response['message']['name']).to eq(['has already been taken']) end it 'should update path & name to existing path & name in different namespace' do project_param = { path: project4.path, name: project4.name } put api("/projects/#{project3.id}", user), project_param - response.status.should == 200 + expect(response.status).to eq(200) project_param.each_pair do |k, v| - json_response[k.to_s].should == v + expect(json_response[k.to_s]).to eq(v) end end end @@ -733,9 +692,9 @@ describe API::API, api: true do it 'should update path' do project_param = { path: 'bar' } put api("/projects/#{project3.id}", user4), project_param - response.status.should == 200 + expect(response.status).to eq(200) project_param.each_pair do |k, v| - json_response[k.to_s].should == v + expect(json_response[k.to_s]).to eq(v) end end @@ -747,29 +706,29 @@ describe API::API, api: true do description: 'new description' } put api("/projects/#{project3.id}", user4), project_param - response.status.should == 200 + expect(response.status).to eq(200) project_param.each_pair do |k, v| - json_response[k.to_s].should == v + expect(json_response[k.to_s]).to eq(v) end end it 'should not update path to existing path' do project_param = { path: project.path } put api("/projects/#{project3.id}", user4), project_param - response.status.should == 400 - json_response['message']['path'].should == ['has already been taken'] + expect(response.status).to eq(400) + expect(json_response['message']['path']).to eq(['has already been taken']) end it 'should not update name' do project_param = { name: 'bar' } put api("/projects/#{project3.id}", user4), project_param - response.status.should == 403 + expect(response.status).to eq(403) end it 'should not update visibility_level' do project_param = { visibility_level: 20 } put api("/projects/#{project3.id}", user4), project_param - response.status.should == 403 + expect(response.status).to eq(403) end end @@ -782,7 +741,7 @@ describe API::API, api: true do merge_requests_enabled: true, description: 'new description' } put api("/projects/#{project.id}", user3), project_param - response.status.should == 403 + expect(response.status).to eq(403) end end end @@ -796,36 +755,36 @@ describe API::API, api: true do ).twice delete api("/projects/#{project.id}", user) - response.status.should == 200 + expect(response.status).to eq(200) end it 'should not remove a project if not an owner' do user3 = create(:user) project.team << [user3, :developer] delete api("/projects/#{project.id}", user3) - response.status.should == 403 + expect(response.status).to eq(403) end it 'should not remove a non existing project' do delete api('/projects/1328', user) - response.status.should == 404 + expect(response.status).to eq(404) end it 'should not remove a project not attached to user' do delete api("/projects/#{project.id}", user2) - response.status.should == 404 + expect(response.status).to eq(404) end end context 'when authenticated as admin' do it 'should remove any existing project' do delete api("/projects/#{project.id}", admin) - response.status.should == 200 + expect(response.status).to eq(200) end it 'should not remove a non existing project' do delete api('/projects/1328', admin) - response.status.should == 404 + expect(response.status).to eq(404) end end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 5518d2df566..729970153d1 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -16,9 +16,9 @@ describe API::API, api: true do describe "GET /projects/:id/repository/tags" do it "should return an array of project tags" do get api("/projects/#{project.id}/repository/tags", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['name'].should == project.repo.tags.sort_by(&:name).reverse.first.name + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(project.repo.tags.sort_by(&:name).reverse.first.name) end end @@ -29,8 +29,8 @@ describe API::API, api: true do tag_name: 'v7.0.1', ref: 'master' - response.status.should == 201 - json_response['name'].should == 'v7.0.1' + expect(response.status).to eq(201) + expect(json_response['name']).to eq('v7.0.1') end end @@ -46,9 +46,9 @@ describe API::API, api: true do ref: 'master', message: 'Release 7.1.0' - response.status.should == 201 - json_response['name'].should == 'v7.1.0' - json_response['message'].should == 'Release 7.1.0' + expect(response.status).to eq(201) + expect(json_response['name']).to eq('v7.1.0') + expect(json_response['message']).to eq('Release 7.1.0') end end @@ -56,35 +56,35 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags", user2), tag_name: 'v1.9.0', ref: '621491c677087aa243f165eab467bfdfbee00be1' - response.status.should == 403 + expect(response.status).to eq(403) end it 'should return 400 if tag name is invalid' do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v 1.0.0', ref: 'master' - response.status.should == 400 - json_response['message'].should == 'Tag name invalid' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Tag name invalid') end it 'should return 400 if tag already exists' do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v8.0.0', ref: 'master' - response.status.should == 201 + expect(response.status).to eq(201) post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v8.0.0', ref: 'master' - response.status.should == 400 - json_response['message'].should == 'Tag already exists' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Tag already exists') end it 'should return 400 if ref name is invalid' do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'mytag', ref: 'foo' - response.status.should == 400 - json_response['message'].should == 'Invalid reference name' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Invalid reference name') end end @@ -94,19 +94,19 @@ describe API::API, api: true do it "should return project commits" do get api("/projects/#{project.id}/repository/tree", user) - response.status.should == 200 + expect(response.status).to eq(200) - json_response.should be_an Array - json_response.first['name'].should == 'encoding' - json_response.first['type'].should == 'tree' - json_response.first['mode'].should == '040000' + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq('encoding') + expect(json_response.first['type']).to eq('tree') + expect(json_response.first['mode']).to eq('040000') end it 'should return a 404 for unknown ref' do get api("/projects/#{project.id}/repository/tree?ref_name=foo", user) - response.status.should == 404 + expect(response.status).to eq(404) - json_response.should be_an Object + expect(json_response).to be_an Object json_response['message'] == '404 Tree Not Found' end end @@ -114,7 +114,7 @@ describe API::API, api: true do context "unauthorized user" do it "should not return project commits" do get api("/projects/#{project.id}/repository/tree") - response.status.should == 401 + expect(response.status).to eq(401) end end end @@ -122,43 +122,43 @@ describe API::API, api: true do describe "GET /projects/:id/repository/blobs/:sha" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return 404 for invalid branch_name" do get api("/projects/#{project.id}/repository/blobs/invalid_branch_name?filepath=README.md", user) - response.status.should == 404 + expect(response.status).to eq(404) end it "should return 404 for invalid file" do get api("/projects/#{project.id}/repository/blobs/master?filepath=README.invalid", user) - response.status.should == 404 + expect(response.status).to eq(404) end it "should return a 400 error if filepath is missing" do get api("/projects/#{project.id}/repository/blobs/master", user) - response.status.should == 400 + expect(response.status).to eq(400) end end describe "GET /projects/:id/repository/commits/:sha/blob" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) - response.status.should == 200 + expect(response.status).to eq(200) end end describe "GET /projects/:id/repository/raw_blobs/:sha" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user) - response.status.should == 200 + expect(response.status).to eq(200) end it 'should return a 404 for unknown blob' do get api("/projects/#{project.id}/repository/raw_blobs/123456", user) - response.status.should == 404 + expect(response.status).to eq(404) - json_response.should be_an Object + expect(json_response).to be_an Object json_response['message'] == '404 Blob Not Found' end end @@ -167,83 +167,83 @@ describe API::API, api: true do it "should get the archive" do get api("/projects/#{project.id}/repository/archive", user) repo_name = project.repository.name.gsub("\.git", "") - response.status.should == 200 - response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/ - response.content_type.should == MIME::Types.type_for('file.tar.gz').first.content_type + expect(response.status).to eq(200) + expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/) + expect(response.content_type).to eq(MIME::Types.type_for('file.tar.gz').first.content_type) end it "should get the archive.zip" do get api("/projects/#{project.id}/repository/archive.zip", user) repo_name = project.repository.name.gsub("\.git", "") - response.status.should == 200 - response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.zip\"/ - response.content_type.should == MIME::Types.type_for('file.zip').first.content_type + expect(response.status).to eq(200) + expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.zip\"/) + expect(response.content_type).to eq(MIME::Types.type_for('file.zip').first.content_type) end it "should get the archive.tar.bz2" do get api("/projects/#{project.id}/repository/archive.tar.bz2", user) repo_name = project.repository.name.gsub("\.git", "") - response.status.should == 200 - response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/ - response.content_type.should == MIME::Types.type_for('file.tar.bz2').first.content_type + expect(response.status).to eq(200) + expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/) + expect(response.content_type).to eq(MIME::Types.type_for('file.tar.bz2').first.content_type) end it "should return 404 for invalid sha" do get api("/projects/#{project.id}/repository/archive/?sha=xxx", user) - response.status.should == 404 + expect(response.status).to eq(404) end end describe 'GET /projects/:id/repository/compare' do it "should compare branches" do get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'feature' - response.status.should == 200 - json_response['commits'].should be_present - json_response['diffs'].should be_present + expect(response.status).to eq(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present end it "should compare tags" do get api("/projects/#{project.id}/repository/compare", user), from: 'v1.0.0', to: 'v1.1.0' - response.status.should == 200 - json_response['commits'].should be_present - json_response['diffs'].should be_present + expect(response.status).to eq(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present end it "should compare commits" do get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.id, to: sample_commit.parent_id - response.status.should == 200 - json_response['commits'].should be_empty - json_response['diffs'].should be_empty - json_response['compare_same_ref'].should be_false + expect(response.status).to eq(200) + expect(json_response['commits']).to be_empty + expect(json_response['diffs']).to be_empty + expect(json_response['compare_same_ref']).to be_falsey end it "should compare commits in reverse order" do get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.parent_id, to: sample_commit.id - response.status.should == 200 - json_response['commits'].should be_present - json_response['diffs'].should be_present + expect(response.status).to eq(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present end it "should compare same refs" do get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'master' - response.status.should == 200 - json_response['commits'].should be_empty - json_response['diffs'].should be_empty - json_response['compare_same_ref'].should be_true + expect(response.status).to eq(200) + expect(json_response['commits']).to be_empty + expect(json_response['diffs']).to be_empty + expect(json_response['compare_same_ref']).to be_truthy end end describe 'GET /projects/:id/repository/contributors' do it 'should return valid data' do get api("/projects/#{project.id}/repository/contributors", user) - response.status.should == 200 - json_response.should be_an Array + expect(response.status).to eq(200) + expect(json_response).to be_an Array contributor = json_response.first - contributor['email'].should == 'dmitriy.zaporozhets@gmail.com' - contributor['name'].should == 'Dmitriy Zaporozhets' - contributor['commits'].should == 13 - contributor['additions'].should == 0 - contributor['deletions'].should == 0 + expect(contributor['email']).to eq('dmitriy.zaporozhets@gmail.com') + expect(contributor['name']).to eq('Dmitriy Zaporozhets') + expect(contributor['commits']).to eq(13) + expect(contributor['additions']).to eq(0) + expect(contributor['deletions']).to eq(0) end end end diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index d8282d0696b..51c543578df 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -9,13 +9,13 @@ describe API::API, api: true do it "should update gitlab-ci settings" do put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'secret-token', project_url: "http://ci.example.com/projects/1" - response.status.should == 200 + expect(response.status).to eq(200) end it "should return if required fields missing" do put api("/projects/#{project.id}/services/gitlab-ci", user), project_url: "http://ci.example.com/projects/1", active: true - response.status.should == 400 + expect(response.status).to eq(400) end end @@ -23,8 +23,8 @@ describe API::API, api: true do it "should update gitlab-ci settings" do delete api("/projects/#{project.id}/services/gitlab-ci", user) - response.status.should == 200 - project.gitlab_ci_service.should be_nil + expect(response.status).to eq(200) + expect(project.gitlab_ci_service).to be_nil end end @@ -33,15 +33,15 @@ describe API::API, api: true do put api("/projects/#{project.id}/services/hipchat", user), token: 'secret-token', room: 'test' - response.status.should == 200 - project.hipchat_service.should_not be_nil + expect(response.status).to eq(200) + expect(project.hipchat_service).not_to be_nil end it 'should return if required fields missing' do put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'secret-token', active: true - response.status.should == 400 + expect(response.status).to eq(400) end end @@ -49,8 +49,8 @@ describe API::API, api: true do it 'should delete hipchat settings' do delete api("/projects/#{project.id}/services/hipchat", user) - response.status.should == 200 - project.hipchat_service.should be_nil + expect(response.status).to eq(200) + expect(project.hipchat_service).to be_nil end end end diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb index 57b2e6cbd6a..fbd57b34a58 100644 --- a/spec/requests/api/session_spec.rb +++ b/spec/requests/api/session_spec.rb @@ -9,13 +9,13 @@ describe API::API, api: true do context "when valid password" do it "should return private token" do post api("/session"), email: user.email, password: '12345678' - response.status.should == 201 + expect(response.status).to eq(201) - json_response['email'].should == user.email - json_response['private_token'].should == user.private_token - json_response['is_admin'].should == user.is_admin? - json_response['can_create_project'].should == user.can_create_project? - json_response['can_create_group'].should == user.can_create_group? + expect(json_response['email']).to eq(user.email) + expect(json_response['private_token']).to eq(user.private_token) + expect(json_response['is_admin']).to eq(user.is_admin?) + expect(json_response['can_create_project']).to eq(user.can_create_project?) + expect(json_response['can_create_group']).to eq(user.can_create_group?) end end @@ -48,30 +48,30 @@ describe API::API, api: true do context "when invalid password" do it "should return authentication error" do post api("/session"), email: user.email, password: '123' - response.status.should == 401 + expect(response.status).to eq(401) - json_response['email'].should be_nil - json_response['private_token'].should be_nil + expect(json_response['email']).to be_nil + expect(json_response['private_token']).to be_nil end end context "when empty password" do it "should return authentication error" do post api("/session"), email: user.email - response.status.should == 401 + expect(response.status).to eq(401) - json_response['email'].should be_nil - json_response['private_token'].should be_nil + expect(json_response['email']).to be_nil + expect(json_response['private_token']).to be_nil end end context "when empty name" do it "should return authentication error" do post api("/session"), password: user.password - response.status.should == 401 + expect(response.status).to eq(401) - json_response['email'].should be_nil - json_response['private_token'].should be_nil + expect(json_response['email']).to be_nil + expect(json_response['private_token']).to be_nil end end end diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 5784ae8c23a..a9d86bbce6c 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -13,23 +13,23 @@ describe API::API, api: true do context "when no user" do it "should return authentication error" do get api("/hooks") - response.status.should == 401 + expect(response.status).to eq(401) end end context "when not an admin" do it "should return forbidden error" do get api("/hooks", user) - response.status.should == 403 + expect(response.status).to eq(403) end end context "when authenticated as admin" do it "should return an array of hooks" do get api("/hooks", admin) - response.status.should == 200 - json_response.should be_an Array - json_response.first['url'].should == hook.url + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['url']).to eq(hook.url) end end end @@ -43,7 +43,7 @@ describe API::API, api: true do it "should respond with 400 if url not given" do post api("/hooks", admin) - response.status.should == 400 + expect(response.status).to eq(400) end it "should not create new hook without url" do @@ -56,13 +56,13 @@ describe API::API, api: true do describe "GET /hooks/:id" do it "should return hook by id" do get api("/hooks/#{hook.id}", admin) - response.status.should == 200 - json_response['event_name'].should == 'project_create' + expect(response.status).to eq(200) + expect(json_response['event_name']).to eq('project_create') end it "should return 404 on failure" do get api("/hooks/404", admin) - response.status.should == 404 + expect(response.status).to eq(404) end end @@ -75,7 +75,7 @@ describe API::API, api: true do it "should return success if hook id not found" do delete api("/hooks/12345", admin) - response.status.should == 200 + expect(response.status).to eq(200) end end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 12dfcacec23..081400cdedd 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -11,30 +11,30 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/users") - response.status.should == 401 + expect(response.status).to eq(401) end end context "when authenticated" do it "should return an array of users" do get api("/users", user) - response.status.should == 200 - json_response.should be_an Array + expect(response.status).to eq(200) + expect(json_response).to be_an Array username = user.username - json_response.detect { + expect(json_response.detect { |user| user['username'] == username - }['username'].should == username + }['username']).to eq(username) end end context "when admin" do it "should return an array of users" do get api("/users", admin) - response.status.should == 200 - json_response.should be_an Array - json_response.first.keys.should include 'email' - json_response.first.keys.should include 'identities' - json_response.first.keys.should include 'can_create_project' + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first.keys).to include 'email' + expect(json_response.first.keys).to include 'identities' + expect(json_response.first.keys).to include 'can_create_project' end end end @@ -42,19 +42,19 @@ describe API::API, api: true do describe "GET /users/:id" do it "should return a user by id" do get api("/users/#{user.id}", user) - response.status.should == 200 - json_response['username'].should == user.username + expect(response.status).to eq(200) + expect(json_response['username']).to eq(user.username) end it "should return a 401 if unauthenticated" do get api("/users/9998") - response.status.should == 401 + expect(response.status).to eq(401) end it "should return a 404 error if user id not found" do get api("/users/9999", user) - response.status.should == 404 - json_response['message'].should == '404 Not found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Not found') end end @@ -69,36 +69,36 @@ describe API::API, api: true do it "should create user with correct attributes" do post api('/users', admin), attributes_for(:user, admin: true, can_create_group: true) - response.status.should == 201 + expect(response.status).to eq(201) user_id = json_response['id'] new_user = User.find(user_id) - new_user.should_not == nil - new_user.admin.should == true - new_user.can_create_group.should == true + expect(new_user).not_to eq(nil) + expect(new_user.admin).to eq(true) + expect(new_user.can_create_group).to eq(true) end it "should create non-admin user" do post api('/users', admin), attributes_for(:user, admin: false, can_create_group: false) - response.status.should == 201 + expect(response.status).to eq(201) user_id = json_response['id'] new_user = User.find(user_id) - new_user.should_not == nil - new_user.admin.should == false - new_user.can_create_group.should == false + expect(new_user).not_to eq(nil) + expect(new_user.admin).to eq(false) + expect(new_user.can_create_group).to eq(false) end it "should create non-admin users by default" do post api('/users', admin), attributes_for(:user) - response.status.should == 201 + expect(response.status).to eq(201) user_id = json_response['id'] new_user = User.find(user_id) - new_user.should_not == nil - new_user.admin.should == false + expect(new_user).not_to eq(nil) + expect(new_user.admin).to eq(false) end it "should return 201 Created on success" do post api("/users", admin), attributes_for(:user, projects_limit: 3) - response.status.should == 201 + expect(response.status).to eq(201) end it "should not create user with invalid email" do @@ -106,22 +106,22 @@ describe API::API, api: true do email: 'invalid email', password: 'password', name: 'test' - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return 400 error if name not given' do post api('/users', admin), email: 'test@example.com', password: 'pass1234' - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return 400 error if password not given' do post api('/users', admin), email: 'test@example.com', name: 'test' - response.status.should == 400 + expect(response.status).to eq(400) end it "should return 400 error if email not given" do post api('/users', admin), password: 'pass1234', name: 'test' - response.status.should == 400 + expect(response.status).to eq(400) end it 'should return 400 error if user does not validate' do @@ -132,20 +132,20 @@ describe API::API, api: true do name: 'test', bio: 'g' * 256, projects_limit: -1 - response.status.should == 400 - json_response['message']['password']. - should == ['is too short (minimum is 8 characters)'] - json_response['message']['bio']. - should == ['is too long (maximum is 255 characters)'] - json_response['message']['projects_limit']. - should == ['must be greater than or equal to 0'] - json_response['message']['username']. - should == [Gitlab::Regex.send(:default_regex_message)] + expect(response.status).to eq(400) + expect(json_response['message']['password']). + to eq(['is too short (minimum is 8 characters)']) + expect(json_response['message']['bio']). + to eq(['is too long (maximum is 255 characters)']) + expect(json_response['message']['projects_limit']). + to eq(['must be greater than or equal to 0']) + expect(json_response['message']['username']). + to eq([Gitlab::Regex.send(:default_regex_message)]) end it "shouldn't available for non admin users" do post api("/users", user), attributes_for(:user) - response.status.should == 403 + expect(response.status).to eq(403) end context 'with existing user' do @@ -165,8 +165,8 @@ describe API::API, api: true do password: 'password', username: 'foo' }.to change { User.count }.by(0) - response.status.should == 409 - json_response['message'].should == 'Email has already been taken' + expect(response.status).to eq(409) + expect(json_response['message']).to eq('Email has already been taken') end it 'should return 409 conflict error if same username exists' do @@ -177,8 +177,8 @@ describe API::API, api: true do password: 'password', username: 'test' end.to change { User.count }.by(0) - response.status.should == 409 - json_response['message'].should == 'Username has already been taken' + expect(response.status).to eq(409) + expect(json_response['message']).to eq('Username has already been taken') end end end @@ -187,8 +187,8 @@ describe API::API, api: true do it "should redirect to sign in page" do get "/users/sign_up" - response.status.should == 302 - response.should redirect_to(new_user_session_path) + expect(response.status).to eq(302) + expect(response).to redirect_to(new_user_session_path) end end @@ -199,55 +199,55 @@ describe API::API, api: true do it "should update user with new bio" do put api("/users/#{user.id}", admin), {bio: 'new test bio'} - response.status.should == 200 - json_response['bio'].should == 'new test bio' - user.reload.bio.should == 'new test bio' + expect(response.status).to eq(200) + expect(json_response['bio']).to eq('new test bio') + expect(user.reload.bio).to eq('new test bio') end it 'should update user with his own email' do put api("/users/#{user.id}", admin), email: user.email - response.status.should == 200 - json_response['email'].should == user.email - user.reload.email.should == user.email + expect(response.status).to eq(200) + expect(json_response['email']).to eq(user.email) + expect(user.reload.email).to eq(user.email) end it 'should update user with his own username' do put api("/users/#{user.id}", admin), username: user.username - response.status.should == 200 - json_response['username'].should == user.username - user.reload.username.should == user.username + expect(response.status).to eq(200) + expect(json_response['username']).to eq(user.username) + expect(user.reload.username).to eq(user.username) end it "should update admin status" do put api("/users/#{user.id}", admin), {admin: true} - response.status.should == 200 - json_response['is_admin'].should == true - user.reload.admin.should == true + expect(response.status).to eq(200) + expect(json_response['is_admin']).to eq(true) + expect(user.reload.admin).to eq(true) end it "should not update admin status" do put api("/users/#{admin_user.id}", admin), {can_create_group: false} - response.status.should == 200 - json_response['is_admin'].should == true - admin_user.reload.admin.should == true - admin_user.can_create_group.should == false + expect(response.status).to eq(200) + expect(json_response['is_admin']).to eq(true) + expect(admin_user.reload.admin).to eq(true) + expect(admin_user.can_create_group).to eq(false) end it "should not allow invalid update" do put api("/users/#{user.id}", admin), {email: 'invalid email'} - response.status.should == 400 - user.reload.email.should_not == 'invalid email' + expect(response.status).to eq(400) + expect(user.reload.email).not_to eq('invalid email') end it "shouldn't available for non admin users" do put api("/users/#{user.id}", user), attributes_for(:user) - response.status.should == 403 + expect(response.status).to eq(403) end it "should return 404 for non-existing user" do put api("/users/999999", admin), {bio: 'update should fail'} - response.status.should == 404 - json_response['message'].should == '404 Not found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Not found') end it 'should return 400 error if user does not validate' do @@ -258,15 +258,15 @@ describe API::API, api: true do name: 'test', bio: 'g' * 256, projects_limit: -1 - response.status.should == 400 - json_response['message']['password']. - should == ['is too short (minimum is 8 characters)'] - json_response['message']['bio']. - should == ['is too long (maximum is 255 characters)'] - json_response['message']['projects_limit']. - should == ['must be greater than or equal to 0'] - json_response['message']['username']. - should == [Gitlab::Regex.send(:default_regex_message)] + expect(response.status).to eq(400) + expect(json_response['message']['password']). + to eq(['is too short (minimum is 8 characters)']) + expect(json_response['message']['bio']). + to eq(['is too long (maximum is 255 characters)']) + expect(json_response['message']['projects_limit']). + to eq(['must be greater than or equal to 0']) + expect(json_response['message']['username']). + to eq([Gitlab::Regex.send(:default_regex_message)]) end context "with existing user" do @@ -278,15 +278,15 @@ describe API::API, api: true do it 'should return 409 conflict error if email address exists' do put api("/users/#{@user.id}", admin), email: 'test@example.com' - response.status.should == 409 - @user.reload.email.should == @user.email + expect(response.status).to eq(409) + expect(@user.reload.email).to eq(@user.email) end it 'should return 409 conflict error if username taken' do @user_id = User.all.last.id put api("/users/#{@user.id}", admin), username: 'test' - response.status.should == 409 - @user.reload.username.should == @user.username + expect(response.status).to eq(409) + expect(@user.reload.username).to eq(@user.username) end end end @@ -296,14 +296,14 @@ describe API::API, api: true do it "should not create invalid ssh key" do post api("/users/#{user.id}/keys", admin), { title: "invalid key" } - response.status.should == 400 - json_response['message'].should == '400 (Bad request) "key" not given' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('400 (Bad request) "key" not given') end it 'should not create key without title' do post api("/users/#{user.id}/keys", admin), key: 'some key' - response.status.should == 400 - json_response['message'].should == '400 (Bad request) "title" not given' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('400 (Bad request) "title" not given') end it "should create ssh key" do @@ -320,24 +320,24 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/users/#{user.id}/keys") - response.status.should == 401 + expect(response.status).to eq(401) end end context 'when authenticated' do it 'should return 404 for non-existing user' do get api('/users/999999/keys', admin) - response.status.should == 404 - json_response['message'].should == '404 User Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 User Not Found') end it 'should return array of ssh keys' do user.keys << key user.save get api("/users/#{user.id}/keys", admin) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == key.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['title']).to eq(key.title) end end end @@ -348,7 +348,7 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do delete api("/users/#{user.id}/keys/42") - response.status.should == 401 + expect(response.status).to eq(401) end end @@ -359,21 +359,21 @@ describe API::API, api: true do expect { delete api("/users/#{user.id}/keys/#{key.id}", admin) }.to change { user.keys.count }.by(-1) - response.status.should == 200 + expect(response.status).to eq(200) end it 'should return 404 error if user not found' do user.keys << key user.save delete api("/users/999999/keys/#{key.id}", admin) - response.status.should == 404 - json_response['message'].should == '404 User Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 User Not Found') end it 'should return 404 error if key not foud' do delete api("/users/#{user.id}/keys/42", admin) - response.status.should == 404 - json_response['message'].should == '404 Key Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Key Not Found') end end end @@ -383,42 +383,42 @@ describe API::API, api: true do it "should delete user" do delete api("/users/#{user.id}", admin) - response.status.should == 200 + expect(response.status).to eq(200) expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound - json_response['email'].should == user.email + expect(json_response['email']).to eq(user.email) end it "should not delete for unauthenticated user" do delete api("/users/#{user.id}") - response.status.should == 401 + expect(response.status).to eq(401) end it "shouldn't available for non admin users" do delete api("/users/#{user.id}", user) - response.status.should == 403 + expect(response.status).to eq(403) end it "should return 404 for non-existing user" do delete api("/users/999999", admin) - response.status.should == 404 - json_response['message'].should == '404 User Not Found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 User Not Found') end end describe "GET /user" do it "should return current user" do get api("/user", user) - response.status.should == 200 - json_response['email'].should == user.email - json_response['is_admin'].should == user.is_admin? - json_response['can_create_project'].should == user.can_create_project? - json_response['can_create_group'].should == user.can_create_group? - json_response['projects_limit'].should == user.projects_limit + expect(response.status).to eq(200) + expect(json_response['email']).to eq(user.email) + expect(json_response['is_admin']).to eq(user.is_admin?) + expect(json_response['can_create_project']).to eq(user.can_create_project?) + expect(json_response['can_create_group']).to eq(user.can_create_group?) + expect(json_response['projects_limit']).to eq(user.projects_limit) end it "should return 401 error if user is unauthenticated" do get api("/user") - response.status.should == 401 + expect(response.status).to eq(401) end end @@ -426,7 +426,7 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/user/keys") - response.status.should == 401 + expect(response.status).to eq(401) end end @@ -435,9 +435,9 @@ describe API::API, api: true do user.keys << key user.save get api("/user/keys", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first["title"].should == key.title + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first["title"]).to eq(key.title) end end end @@ -447,14 +447,14 @@ describe API::API, api: true do user.keys << key user.save get api("/user/keys/#{key.id}", user) - response.status.should == 200 - json_response["title"].should == key.title + expect(response.status).to eq(200) + expect(json_response["title"]).to eq(key.title) end it "should return 404 Not Found within invalid ID" do get api("/user/keys/42", user) - response.status.should == 404 - json_response['message'].should == '404 Not found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Not found') end it "should return 404 error if admin accesses user's ssh key" do @@ -462,8 +462,8 @@ describe API::API, api: true do user.save admin get api("/user/keys/#{key.id}", admin) - response.status.should == 404 - json_response['message'].should == '404 Not found' + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Not found') end end @@ -473,29 +473,29 @@ describe API::API, api: true do expect { post api("/user/keys", user), key_attrs }.to change{ user.keys.count }.by(1) - response.status.should == 201 + expect(response.status).to eq(201) end it "should return a 401 error if unauthorized" do post api("/user/keys"), title: 'some title', key: 'some key' - response.status.should == 401 + expect(response.status).to eq(401) end it "should not create ssh key without key" do post api("/user/keys", user), title: 'title' - response.status.should == 400 - json_response['message'].should == '400 (Bad request) "key" not given' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('400 (Bad request) "key" not given') end it 'should not create ssh key without title' do post api('/user/keys', user), key: 'some key' - response.status.should == 400 - json_response['message'].should == '400 (Bad request) "title" not given' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('400 (Bad request) "title" not given') end it "should not create ssh key without title" do post api("/user/keys", user), key: "somekey" - response.status.should == 400 + expect(response.status).to eq(400) end end @@ -506,19 +506,19 @@ describe API::API, api: true do expect { delete api("/user/keys/#{key.id}", user) }.to change{user.keys.count}.by(-1) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return success if key ID not found" do delete api("/user/keys/42", user) - response.status.should == 200 + expect(response.status).to eq(200) end it "should return 401 error if unauthorized" do user.keys << key user.save delete api("/user/keys/#{key.id}") - response.status.should == 401 + expect(response.status).to eq(401) end end end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 7fe18ff47c3..bf8abcfb00f 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -12,47 +12,47 @@ require 'spec_helper' # DELETE /admin/users/:id(.:format) admin/users#destroy describe Admin::UsersController, "routing" do it "to #team_update" do - put("/admin/users/1/team_update").should route_to('admin/users#team_update', id: '1') + expect(put("/admin/users/1/team_update")).to route_to('admin/users#team_update', id: '1') end it "to #block" do - put("/admin/users/1/block").should route_to('admin/users#block', id: '1') + expect(put("/admin/users/1/block")).to route_to('admin/users#block', id: '1') end it "to #unblock" do - put("/admin/users/1/unblock").should route_to('admin/users#unblock', id: '1') + expect(put("/admin/users/1/unblock")).to route_to('admin/users#unblock', id: '1') end it "to #index" do - get("/admin/users").should route_to('admin/users#index') + expect(get("/admin/users")).to route_to('admin/users#index') end it "to #show" do - get("/admin/users/1").should route_to('admin/users#show', id: '1') + expect(get("/admin/users/1")).to route_to('admin/users#show', id: '1') end it "to #create" do - post("/admin/users").should route_to('admin/users#create') + expect(post("/admin/users")).to route_to('admin/users#create') end it "to #new" do - get("/admin/users/new").should route_to('admin/users#new') + expect(get("/admin/users/new")).to route_to('admin/users#new') end it "to #edit" do - get("/admin/users/1/edit").should route_to('admin/users#edit', id: '1') + expect(get("/admin/users/1/edit")).to route_to('admin/users#edit', id: '1') end it "to #show" do - get("/admin/users/1").should route_to('admin/users#show', id: '1') + expect(get("/admin/users/1")).to route_to('admin/users#show', id: '1') end it "to #update" do - put("/admin/users/1").should route_to('admin/users#update', id: '1') + expect(put("/admin/users/1")).to route_to('admin/users#update', id: '1') end it "to #destroy" do - delete("/admin/users/1").should route_to('admin/users#destroy', id: '1') + expect(delete("/admin/users/1")).to route_to('admin/users#destroy', id: '1') end end @@ -67,11 +67,11 @@ end # DELETE /admin/projects/:id(.:format) admin/projects#destroy {id: /[^\/]+/} describe Admin::ProjectsController, "routing" do it "to #index" do - get("/admin/projects").should route_to('admin/projects#index') + expect(get("/admin/projects")).to route_to('admin/projects#index') end it "to #show" do - get("/admin/projects/gitlab").should route_to('admin/projects#show', id: 'gitlab') + expect(get("/admin/projects/gitlab")).to route_to('admin/projects#show', namespace_id: 'gitlab') end end @@ -81,19 +81,19 @@ end # admin_hook DELETE /admin/hooks/:id(.:format) admin/hooks#destroy describe Admin::HooksController, "routing" do it "to #test" do - get("/admin/hooks/1/test").should route_to('admin/hooks#test', hook_id: '1') + expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', hook_id: '1') end it "to #index" do - get("/admin/hooks").should route_to('admin/hooks#index') + expect(get("/admin/hooks")).to route_to('admin/hooks#index') end it "to #create" do - post("/admin/hooks").should route_to('admin/hooks#create') + expect(post("/admin/hooks")).to route_to('admin/hooks#create') end it "to #destroy" do - delete("/admin/hooks/1").should route_to('admin/hooks#destroy', id: '1') + expect(delete("/admin/hooks/1")).to route_to('admin/hooks#destroy', id: '1') end end @@ -101,21 +101,21 @@ end # admin_logs GET /admin/logs(.:format) admin/logs#show describe Admin::LogsController, "routing" do it "to #show" do - get("/admin/logs").should route_to('admin/logs#show') + expect(get("/admin/logs")).to route_to('admin/logs#show') end end # admin_background_jobs GET /admin/background_jobs(.:format) admin/background_jobs#show describe Admin::BackgroundJobsController, "routing" do it "to #show" do - get("/admin/background_jobs").should route_to('admin/background_jobs#show') + expect(get("/admin/background_jobs")).to route_to('admin/background_jobs#show') end end # admin_root /admin(.:format) admin/dashboard#index describe Admin::DashboardController, "routing" do it "to #index" do - get("/admin").should route_to('admin/dashboard#index') + expect(get("/admin")).to route_to('admin/dashboard#index') end end diff --git a/spec/routing/notifications_routing_spec.rb b/spec/routing/notifications_routing_spec.rb index 112b825e023..24592942a96 100644 --- a/spec/routing/notifications_routing_spec.rb +++ b/spec/routing/notifications_routing_spec.rb @@ -3,11 +3,11 @@ require "spec_helper" describe Profiles::NotificationsController do describe "routing" do it "routes to #show" do - get("/profile/notifications").should route_to("profiles/notifications#show") + expect(get("/profile/notifications")).to route_to("profiles/notifications#show") end it "routes to #update" do - put("/profile/notifications").should route_to("profiles/notifications#update") + expect(put("/profile/notifications")).to route_to("profiles/notifications#update") end end end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index b8f9d2bf20a..4308a765b56 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -25,31 +25,31 @@ shared_examples 'RESTful project resources' do let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] } it 'to #index' do - get("/gitlab/gitlabhq/#{controller}").should route_to("projects/#{controller}#index", project_id: 'gitlab/gitlabhq') if actions.include?(:index) + expect(get("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index) end it 'to #create' do - post("/gitlab/gitlabhq/#{controller}").should route_to("projects/#{controller}#create", project_id: 'gitlab/gitlabhq') if actions.include?(:create) + expect(post("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create) end it 'to #new' do - get("/gitlab/gitlabhq/#{controller}/new").should route_to("projects/#{controller}#new", project_id: 'gitlab/gitlabhq') if actions.include?(:new) + expect(get("/gitlab/gitlabhq/#{controller}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new) end it 'to #edit' do - get("/gitlab/gitlabhq/#{controller}/1/edit").should route_to("projects/#{controller}#edit", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:edit) + expect(get("/gitlab/gitlabhq/#{controller}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit) end it 'to #show' do - get("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#show", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:show) + expect(get("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show) end it 'to #update' do - put("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#update", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:update) + expect(put("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update) end it 'to #destroy' do - delete("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#destroy", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:destroy) + expect(delete("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy) end end @@ -63,36 +63,36 @@ end # markdown_preview_project POST /:id/markdown_preview(.:format) projects#markdown_preview describe ProjectsController, 'routing' do it 'to #create' do - post('/projects').should route_to('projects#create') + expect(post('/projects')).to route_to('projects#create') end it 'to #new' do - get('/projects/new').should route_to('projects#new') + expect(get('/projects/new')).to route_to('projects#new') end it 'to #edit' do - get('/gitlab/gitlabhq/edit').should route_to('projects#edit', id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/edit')).to route_to('projects#edit', namespace_id: 'gitlab', id: 'gitlabhq') end it 'to #autocomplete_sources' do - get('/gitlab/gitlabhq/autocomplete_sources').should route_to('projects#autocomplete_sources', id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/autocomplete_sources')).to route_to('projects#autocomplete_sources', namespace_id: 'gitlab', id: 'gitlabhq') end it 'to #show' do - get('/gitlab/gitlabhq').should route_to('projects#show', id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq') end it 'to #update' do - put('/gitlab/gitlabhq').should route_to('projects#update', id: 'gitlab/gitlabhq') + expect(put('/gitlab/gitlabhq')).to route_to('projects#update', namespace_id: 'gitlab', id: 'gitlabhq') end it 'to #destroy' do - delete('/gitlab/gitlabhq').should route_to('projects#destroy', id: 'gitlab/gitlabhq') + expect(delete('/gitlab/gitlabhq')).to route_to('projects#destroy', namespace_id: 'gitlab', id: 'gitlabhq') end it 'to #markdown_preview' do - post('/gitlab/gitlabhq/markdown_preview').should( - route_to('projects#markdown_preview', id: 'gitlab/gitlabhq') + expect(post('/gitlab/gitlabhq/markdown_preview')).to( + route_to('projects#markdown_preview', namespace_id: 'gitlab', id: 'gitlabhq') ) end end @@ -105,11 +105,11 @@ end # DELETE /:project_id/wikis/:id(.:format) projects/wikis#destroy describe Projects::WikisController, 'routing' do it 'to #pages' do - get('/gitlab/gitlabhq/wikis/pages').should route_to('projects/wikis#pages', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/wikis/pages')).to route_to('projects/wikis#pages', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #history' do - get('/gitlab/gitlabhq/wikis/1/history').should route_to('projects/wikis#history', project_id: 'gitlab/gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it_behaves_like 'RESTful project resources' do @@ -124,43 +124,43 @@ end # edit_project_repository GET /:project_id/repository/edit(.:format) projects/repositories#edit describe Projects::RepositoriesController, 'routing' do it 'to #archive' do - get('/gitlab/gitlabhq/repository/archive').should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/repository/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #archive format:zip' do - get('/gitlab/gitlabhq/repository/archive.zip').should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'zip') + expect(get('/gitlab/gitlabhq/repository/archive.zip')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'zip') end it 'to #archive format:tar.bz2' do - get('/gitlab/gitlabhq/repository/archive.tar.bz2').should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'tar.bz2') + expect(get('/gitlab/gitlabhq/repository/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2') end it 'to #show' do - get('/gitlab/gitlabhq/repository').should route_to('projects/repositories#show', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/repository')).to route_to('projects/repositories#show', namespace_id: 'gitlab', project_id: 'gitlabhq') end end describe Projects::BranchesController, 'routing' do it 'to #branches' do - get('/gitlab/gitlabhq/branches').should route_to('projects/branches#index', project_id: 'gitlab/gitlabhq') - delete('/gitlab/gitlabhq/branches/feature%2345').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45') - delete('/gitlab/gitlabhq/branches/feature%2B45').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45') - delete('/gitlab/gitlabhq/branches/feature@45').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45') - delete('/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz') - delete('/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz') - delete('/gitlab/gitlabhq/branches/feature@45/foo/bar/baz').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz') + expect(get('/gitlab/gitlabhq/branches')).to route_to('projects/branches#index', namespace_id: 'gitlab', project_id: 'gitlabhq') + expect(delete('/gitlab/gitlabhq/branches/feature%2345')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45') + expect(delete('/gitlab/gitlabhq/branches/feature%2B45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45') + expect(delete('/gitlab/gitlabhq/branches/feature@45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45') + expect(delete('/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/branches/feature@45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz') end end describe Projects::TagsController, 'routing' do it 'to #tags' do - get('/gitlab/gitlabhq/tags').should route_to('projects/tags#index', project_id: 'gitlab/gitlabhq') - delete('/gitlab/gitlabhq/tags/feature%2345').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45') - delete('/gitlab/gitlabhq/tags/feature%2B45').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45') - delete('/gitlab/gitlabhq/tags/feature@45').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45') - delete('/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz') - delete('/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz') - delete('/gitlab/gitlabhq/tags/feature@45/foo/bar/baz').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz') + expect(get('/gitlab/gitlabhq/tags')).to route_to('projects/tags#index', namespace_id: 'gitlab', project_id: 'gitlabhq') + expect(delete('/gitlab/gitlabhq/tags/feature%2345')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45') + expect(delete('/gitlab/gitlabhq/tags/feature%2B45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45') + expect(delete('/gitlab/gitlabhq/tags/feature@45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45') + expect(delete('/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/tags/feature@45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz') end end @@ -193,19 +193,19 @@ end # logs_file_project_ref GET /:project_id/refs/:id/logs_tree/:path(.:format) refs#logs_tree describe Projects::RefsController, 'routing' do it 'to #switch' do - get('/gitlab/gitlabhq/refs/switch').should route_to('projects/refs#switch', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/refs/switch')).to route_to('projects/refs#switch', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #logs_tree' do - get('/gitlab/gitlabhq/refs/stable/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable') - get('/gitlab/gitlabhq/refs/feature%2345/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45') - get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45') - get('/gitlab/gitlabhq/refs/feature@45/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45') - get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'foo/bar/baz') - get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45', path: 'foo/bar/baz') - get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45', path: 'foo/bar/baz') - get('/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45', path: 'foo/bar/baz') - get('/gitlab/gitlabhq/refs/stable/logs_tree/files.scss').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss') + expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable') + expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45') + expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45') + expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45') + expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz') + expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45', path: 'foo/bar/baz') + expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz') + expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45', path: 'foo/bar/baz') + expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/files.scss')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'files.scss') end end @@ -223,31 +223,31 @@ end # DELETE /:project_id/merge_requests/:id(.:format) projects/merge_requests#destroy describe Projects::MergeRequestsController, 'routing' do it 'to #diffs' do - get('/gitlab/gitlabhq/merge_requests/1/diffs').should route_to('projects/merge_requests#diffs', project_id: 'gitlab/gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/merge_requests/1/diffs')).to route_to('projects/merge_requests#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #automerge' do - post('/gitlab/gitlabhq/merge_requests/1/automerge').should route_to( + expect(post('/gitlab/gitlabhq/merge_requests/1/automerge')).to route_to( 'projects/merge_requests#automerge', - project_id: 'gitlab/gitlabhq', id: '1' + namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1' ) end it 'to #automerge_check' do - get('/gitlab/gitlabhq/merge_requests/1/automerge_check').should route_to('projects/merge_requests#automerge_check', project_id: 'gitlab/gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/merge_requests/1/automerge_check')).to route_to('projects/merge_requests#automerge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #branch_from' do - get('/gitlab/gitlabhq/merge_requests/branch_from').should route_to('projects/merge_requests#branch_from', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/merge_requests/branch_from')).to route_to('projects/merge_requests#branch_from', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #branch_to' do - get('/gitlab/gitlabhq/merge_requests/branch_to').should route_to('projects/merge_requests#branch_to', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/merge_requests/branch_to')).to route_to('projects/merge_requests#branch_to', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #show' do - get('/gitlab/gitlabhq/merge_requests/1.diff').should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'diff') - get('/gitlab/gitlabhq/merge_requests/1.patch').should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'patch') + expect(get('/gitlab/gitlabhq/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff') + expect(get('/gitlab/gitlabhq/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch') end it_behaves_like 'RESTful project resources' do @@ -266,35 +266,35 @@ end # DELETE /:project_id/snippets/:id(.:format) snippets#destroy describe SnippetsController, 'routing' do it 'to #raw' do - get('/gitlab/gitlabhq/snippets/1/raw').should route_to('projects/snippets#raw', project_id: 'gitlab/gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/snippets/1/raw')).to route_to('projects/snippets#raw', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #index' do - get('/gitlab/gitlabhq/snippets').should route_to('projects/snippets#index', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#index', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #create' do - post('/gitlab/gitlabhq/snippets').should route_to('projects/snippets#create', project_id: 'gitlab/gitlabhq') + expect(post('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#create', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #new' do - get('/gitlab/gitlabhq/snippets/new').should route_to('projects/snippets#new', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/snippets/new')).to route_to('projects/snippets#new', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #edit' do - get('/gitlab/gitlabhq/snippets/1/edit').should route_to('projects/snippets#edit', project_id: 'gitlab/gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/snippets/1/edit')).to route_to('projects/snippets#edit', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #show' do - get('/gitlab/gitlabhq/snippets/1').should route_to('projects/snippets#show', project_id: 'gitlab/gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #update' do - put('/gitlab/gitlabhq/snippets/1').should route_to('projects/snippets#update', project_id: 'gitlab/gitlabhq', id: '1') + expect(put('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#update', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #destroy' do - delete('/gitlab/gitlabhq/snippets/1').should route_to('projects/snippets#destroy', project_id: 'gitlab/gitlabhq', id: '1') + expect(delete('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end end @@ -304,7 +304,7 @@ end # project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy describe Projects::HooksController, 'routing' do it 'to #test' do - get('/gitlab/gitlabhq/hooks/1/test').should route_to('projects/hooks#test', project_id: 'gitlab/gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it_behaves_like 'RESTful project resources' do @@ -316,10 +316,10 @@ end # project_commit GET /:project_id/commit/:id(.:format) commit#show {id: /[[:alnum:]]{6,40}/, project_id: /[^\/]+/} describe Projects::CommitController, 'routing' do it 'to #show' do - get('/gitlab/gitlabhq/commit/4246fb').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb') - get('/gitlab/gitlabhq/commit/4246fb.diff').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'diff') - get('/gitlab/gitlabhq/commit/4246fb.patch').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'patch') - get('/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5') + expect(get('/gitlab/gitlabhq/commit/4246fb')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fb') + expect(get('/gitlab/gitlabhq/commit/4246fb.diff')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fb', format: 'diff') + expect(get('/gitlab/gitlabhq/commit/4246fb.patch')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fb', format: 'patch') + expect(get('/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5') end end @@ -334,7 +334,7 @@ describe Projects::CommitsController, 'routing' do end it 'to #show' do - get('/gitlab/gitlabhq/commits/master.atom').should route_to('projects/commits#show', project_id: 'gitlab/gitlabhq', id: 'master', format: 'atom') + expect(get('/gitlab/gitlabhq/commits/master.atom')).to route_to('projects/commits#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'atom') end end @@ -369,7 +369,7 @@ end # project_labels GET /:project_id/labels(.:format) labels#index describe Projects::LabelsController, 'routing' do it 'to #index' do - get('/gitlab/gitlabhq/labels').should route_to('projects/labels#index', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/labels')).to route_to('projects/labels#index', namespace_id: 'gitlab', project_id: 'gitlabhq') end end @@ -385,7 +385,7 @@ end # DELETE /:project_id/issues/:id(.:format) issues#destroy describe Projects::IssuesController, 'routing' do it 'to #bulk_update' do - post('/gitlab/gitlabhq/issues/bulk_update').should route_to('projects/issues#bulk_update', project_id: 'gitlab/gitlabhq') + expect(post('/gitlab/gitlabhq/issues/bulk_update')).to route_to('projects/issues#bulk_update', namespace_id: 'gitlab', project_id: 'gitlabhq') end it_behaves_like 'RESTful project resources' do @@ -407,41 +407,41 @@ end # project_blame GET /:project_id/blame/:id(.:format) blame#show {id: /.+/, project_id: /[^\/]+/} describe Projects::BlameController, 'routing' do it 'to #show' do - get('/gitlab/gitlabhq/blame/master/app/models/project.rb').should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb') - get('/gitlab/gitlabhq/blame/master/files.scss').should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') + expect(get('/gitlab/gitlabhq/blame/master/app/models/project.rb')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb') + expect(get('/gitlab/gitlabhq/blame/master/files.scss')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss') end end # project_blob GET /:project_id/blob/:id(.:format) blob#show {id: /.+/, project_id: /[^\/]+/} describe Projects::BlobController, 'routing' do it 'to #show' do - get('/gitlab/gitlabhq/blob/master/app/models/project.rb').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb') - get('/gitlab/gitlabhq/blob/master/app/models/compare.rb').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/compare.rb') - get('/gitlab/gitlabhq/blob/master/app/models/diff.js').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/diff.js') - get('/gitlab/gitlabhq/blob/master/files.scss').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') + expect(get('/gitlab/gitlabhq/blob/master/app/models/project.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb') + expect(get('/gitlab/gitlabhq/blob/master/app/models/compare.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/compare.rb') + expect(get('/gitlab/gitlabhq/blob/master/app/models/diff.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/diff.js') + expect(get('/gitlab/gitlabhq/blob/master/files.scss')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss') end end # project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /.+/, project_id: /[^\/]+/} describe Projects::TreeController, 'routing' do it 'to #show' do - get('/gitlab/gitlabhq/tree/master/app/models/project.rb').should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb') - get('/gitlab/gitlabhq/tree/master/files.scss').should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss') + expect(get('/gitlab/gitlabhq/tree/master/app/models/project.rb')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb') + expect(get('/gitlab/gitlabhq/tree/master/files.scss')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss') end end describe Projects::BlobController, 'routing' do it 'to #edit' do - get('/gitlab/gitlabhq/edit/master/app/models/project.rb').should( + expect(get('/gitlab/gitlabhq/edit/master/app/models/project.rb')).to( route_to('projects/blob#edit', - project_id: 'gitlab/gitlabhq', + namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')) end it 'to #preview' do - post('/gitlab/gitlabhq/preview/master/app/models/project.rb').should( + expect(post('/gitlab/gitlabhq/preview/master/app/models/project.rb')).to( route_to('projects/blob#preview', - project_id: 'gitlab/gitlabhq', + namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')) end end @@ -451,46 +451,46 @@ end # project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/} describe Projects::CompareController, 'routing' do it 'to #index' do - get('/gitlab/gitlabhq/compare').should route_to('projects/compare#index', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/compare')).to route_to('projects/compare#index', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #compare' do - post('/gitlab/gitlabhq/compare').should route_to('projects/compare#create', project_id: 'gitlab/gitlabhq') + expect(post('/gitlab/gitlabhq/compare')).to route_to('projects/compare#create', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #show' do - get('/gitlab/gitlabhq/compare/master...stable').should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'master', to: 'stable') - get('/gitlab/gitlabhq/compare/issue/1234...stable').should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'issue/1234', to: 'stable') + expect(get('/gitlab/gitlabhq/compare/master...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'master', to: 'stable') + expect(get('/gitlab/gitlabhq/compare/issue/1234...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'issue/1234', to: 'stable') end end describe Projects::NetworkController, 'routing' do it 'to #show' do - get('/gitlab/gitlabhq/network/master').should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master') - get('/gitlab/gitlabhq/network/master.json').should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master', format: 'json') + expect(get('/gitlab/gitlabhq/network/master')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') + expect(get('/gitlab/gitlabhq/network/master.json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json') end end describe Projects::GraphsController, 'routing' do it 'to #show' do - get('/gitlab/gitlabhq/graphs/master').should route_to('projects/graphs#show', project_id: 'gitlab/gitlabhq', id: 'master') + expect(get('/gitlab/gitlabhq/graphs/master')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') end end describe Projects::ForksController, 'routing' do it 'to #new' do - get('/gitlab/gitlabhq/fork/new').should route_to('projects/forks#new', project_id: 'gitlab/gitlabhq') + expect(get('/gitlab/gitlabhq/fork/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq') end it 'to #create' do - post('/gitlab/gitlabhq/fork').should route_to('projects/forks#create', project_id: 'gitlab/gitlabhq') + expect(post('/gitlab/gitlabhq/fork')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq') end end # project_avatar DELETE /project/avatar(.:format) projects/avatars#destroy describe Projects::AvatarsController, 'routing' do it 'to #destroy' do - delete('/gitlab/gitlabhq/avatar').should route_to( - 'projects/avatars#destroy', project_id: 'gitlab/gitlabhq') + expect(delete('/gitlab/gitlabhq/avatar')).to route_to( + 'projects/avatars#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq') end end diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 1e92cf62dd5..d4915b51952 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' # search GET /search(.:format) search#show describe SearchController, "routing" do it "to #show" do - get("/search").should route_to('search#show') + expect(get("/search")).to route_to('search#show') end end @@ -11,11 +11,11 @@ end # /:path Grack describe "Mounted Apps", "routing" do it "to API" do - get("/api/issues").should be_routable + expect(get("/api/issues")).to be_routable end it "to Grack" do - get("/gitlab/gitlabhq.git").should be_routable + expect(get("/gitlab/gitlabhq.git")).to be_routable end end @@ -28,39 +28,39 @@ end # DELETE /snippets/:id(.:format) snippets#destroy describe SnippetsController, "routing" do it "to #user_index" do - get("/s/User").should route_to('snippets#user_index', username: 'User') + expect(get("/s/User")).to route_to('snippets#user_index', username: 'User') end it "to #raw" do - get("/snippets/1/raw").should route_to('snippets#raw', id: '1') + expect(get("/snippets/1/raw")).to route_to('snippets#raw', id: '1') end it "to #index" do - get("/snippets").should route_to('snippets#index') + expect(get("/snippets")).to route_to('snippets#index') end it "to #create" do - post("/snippets").should route_to('snippets#create') + expect(post("/snippets")).to route_to('snippets#create') end it "to #new" do - get("/snippets/new").should route_to('snippets#new') + expect(get("/snippets/new")).to route_to('snippets#new') end it "to #edit" do - get("/snippets/1/edit").should route_to('snippets#edit', id: '1') + expect(get("/snippets/1/edit")).to route_to('snippets#edit', id: '1') end it "to #show" do - get("/snippets/1").should route_to('snippets#show', id: '1') + expect(get("/snippets/1")).to route_to('snippets#show', id: '1') end it "to #update" do - put("/snippets/1").should route_to('snippets#update', id: '1') + expect(put("/snippets/1")).to route_to('snippets#update', id: '1') end it "to #destroy" do - delete("/snippets/1").should route_to('snippets#destroy', id: '1') + expect(delete("/snippets/1")).to route_to('snippets#destroy', id: '1') end end @@ -75,39 +75,39 @@ end # help_raketasks GET /help/raketasks(.:format) help#raketasks describe HelpController, "routing" do it "to #index" do - get("/help").should route_to('help#index') + expect(get("/help")).to route_to('help#index') end it "to #permissions" do - get("/help/permissions/permissions").should route_to('help#show', category: "permissions", file: "permissions") + expect(get("/help/permissions/permissions")).to route_to('help#show', category: "permissions", file: "permissions") end it "to #workflow" do - get("/help/workflow/README").should route_to('help#show', category: "workflow", file: "README") + expect(get("/help/workflow/README")).to route_to('help#show', category: "workflow", file: "README") end it "to #api" do - get("/help/api/README").should route_to('help#show', category: "api", file: "README") + expect(get("/help/api/README")).to route_to('help#show', category: "api", file: "README") end it "to #web_hooks" do - get("/help/web_hooks/web_hooks").should route_to('help#show', category: "web_hooks", file: "web_hooks") + expect(get("/help/web_hooks/web_hooks")).to route_to('help#show', category: "web_hooks", file: "web_hooks") end it "to #system_hooks" do - get("/help/system_hooks/system_hooks").should route_to('help#show', category: "system_hooks", file: "system_hooks") + expect(get("/help/system_hooks/system_hooks")).to route_to('help#show', category: "system_hooks", file: "system_hooks") end it "to #markdown" do - get("/help/markdown/markdown").should route_to('help#show',category: "markdown", file: "markdown") + expect(get("/help/markdown/markdown")).to route_to('help#show',category: "markdown", file: "markdown") end it "to #ssh" do - get("/help/ssh/README").should route_to('help#show', category: "ssh", file: "README") + expect(get("/help/ssh/README")).to route_to('help#show', category: "ssh", file: "README") end it "to #raketasks" do - get("/help/raketasks/README").should route_to('help#show', category: "raketasks", file: "README") + expect(get("/help/raketasks/README")).to route_to('help#show', category: "raketasks", file: "README") end end @@ -121,23 +121,23 @@ end # profile_update PUT /profile/update(.:format) profile#update describe ProfilesController, "routing" do it "to #account" do - get("/profile/account").should route_to('profiles/accounts#show') + expect(get("/profile/account")).to route_to('profiles/accounts#show') end it "to #history" do - get("/profile/history").should route_to('profiles#history') + expect(get("/profile/history")).to route_to('profiles#history') end it "to #reset_private_token" do - put("/profile/reset_private_token").should route_to('profiles#reset_private_token') + expect(put("/profile/reset_private_token")).to route_to('profiles#reset_private_token') end it "to #show" do - get("/profile").should route_to('profiles#show') + expect(get("/profile")).to route_to('profiles#show') end it "to #design" do - get("/profile/design").should route_to('profiles#design') + expect(get("/profile/design")).to route_to('profiles#design') end end @@ -150,36 +150,36 @@ end # DELETE /keys/:id(.:format) keys#destroy describe Profiles::KeysController, "routing" do it "to #index" do - get("/profile/keys").should route_to('profiles/keys#index') + expect(get("/profile/keys")).to route_to('profiles/keys#index') end it "to #create" do - post("/profile/keys").should route_to('profiles/keys#create') + expect(post("/profile/keys")).to route_to('profiles/keys#create') end it "to #new" do - get("/profile/keys/new").should route_to('profiles/keys#new') + expect(get("/profile/keys/new")).to route_to('profiles/keys#new') end it "to #edit" do - get("/profile/keys/1/edit").should route_to('profiles/keys#edit', id: '1') + expect(get("/profile/keys/1/edit")).to route_to('profiles/keys#edit', id: '1') end it "to #show" do - get("/profile/keys/1").should route_to('profiles/keys#show', id: '1') + expect(get("/profile/keys/1")).to route_to('profiles/keys#show', id: '1') end it "to #update" do - put("/profile/keys/1").should route_to('profiles/keys#update', id: '1') + expect(put("/profile/keys/1")).to route_to('profiles/keys#update', id: '1') end it "to #destroy" do - delete("/profile/keys/1").should route_to('profiles/keys#destroy', id: '1') + expect(delete("/profile/keys/1")).to route_to('profiles/keys#destroy', id: '1') end # get all the ssh-keys of a user it "to #get_keys" do - get("/foo.keys").should route_to('profiles/keys#get_keys', username: 'foo') + expect(get("/foo.keys")).to route_to('profiles/keys#get_keys', username: 'foo') end end @@ -188,22 +188,22 @@ end # DELETE /keys/:id(.:format) keys#destroy describe Profiles::EmailsController, "routing" do it "to #index" do - get("/profile/emails").should route_to('profiles/emails#index') + expect(get("/profile/emails")).to route_to('profiles/emails#index') end it "to #create" do - post("/profile/emails").should route_to('profiles/emails#create') + expect(post("/profile/emails")).to route_to('profiles/emails#create') end it "to #destroy" do - delete("/profile/emails/1").should route_to('profiles/emails#destroy', id: '1') + expect(delete("/profile/emails/1")).to route_to('profiles/emails#destroy', id: '1') end end # profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy describe Profiles::AvatarsController, "routing" do it "to #destroy" do - delete("/profile/avatar").should route_to('profiles/avatars#destroy') + expect(delete("/profile/avatar")).to route_to('profiles/avatars#destroy') end end @@ -213,16 +213,16 @@ end # root / dashboard#show describe DashboardController, "routing" do it "to #index" do - get("/dashboard").should route_to('dashboard#show') - get("/").should route_to('dashboard#show') + expect(get("/dashboard")).to route_to('dashboard#show') + expect(get("/")).to route_to('dashboard#show') end it "to #issues" do - get("/dashboard/issues").should route_to('dashboard#issues') + expect(get("/dashboard/issues")).to route_to('dashboard#issues') end it "to #merge_requests" do - get("/dashboard/merge_requests").should route_to('dashboard#merge_requests') + expect(get("/dashboard/merge_requests")).to route_to('dashboard#merge_requests') end end @@ -241,11 +241,11 @@ end describe "Groups", "routing" do it "to #show" do - get("/groups/1").should route_to('groups#show', id: '1') + expect(get("/groups/1")).to route_to('groups#show', id: '1') end it "also display group#show on the short path" do - get('/1').should route_to('namespaces#show', id: '1') + expect(get('/1')).to route_to('namespaces#show', id: '1') end end diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index 713aa3e7e74..007a9eed192 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -7,7 +7,7 @@ describe EventCreateService do describe :open_issue do let(:issue) { create(:issue) } - it { service.open_issue(issue, issue.author).should be_true } + it { expect(service.open_issue(issue, issue.author)).to be_truthy } it "should create new event" do expect { service.open_issue(issue, issue.author) }.to change { Event.count } @@ -17,7 +17,7 @@ describe EventCreateService do describe :close_issue do let(:issue) { create(:issue) } - it { service.close_issue(issue, issue.author).should be_true } + it { expect(service.close_issue(issue, issue.author)).to be_truthy } it "should create new event" do expect { service.close_issue(issue, issue.author) }.to change { Event.count } @@ -27,7 +27,7 @@ describe EventCreateService do describe :reopen_issue do let(:issue) { create(:issue) } - it { service.reopen_issue(issue, issue.author).should be_true } + it { expect(service.reopen_issue(issue, issue.author)).to be_truthy } it "should create new event" do expect { service.reopen_issue(issue, issue.author) }.to change { Event.count } @@ -39,7 +39,7 @@ describe EventCreateService do describe :open_mr do let(:merge_request) { create(:merge_request) } - it { service.open_mr(merge_request, merge_request.author).should be_true } + it { expect(service.open_mr(merge_request, merge_request.author)).to be_truthy } it "should create new event" do expect { service.open_mr(merge_request, merge_request.author) }.to change { Event.count } @@ -49,7 +49,7 @@ describe EventCreateService do describe :close_mr do let(:merge_request) { create(:merge_request) } - it { service.close_mr(merge_request, merge_request.author).should be_true } + it { expect(service.close_mr(merge_request, merge_request.author)).to be_truthy } it "should create new event" do expect { service.close_mr(merge_request, merge_request.author) }.to change { Event.count } @@ -59,7 +59,7 @@ describe EventCreateService do describe :merge_mr do let(:merge_request) { create(:merge_request) } - it { service.merge_mr(merge_request, merge_request.author).should be_true } + it { expect(service.merge_mr(merge_request, merge_request.author)).to be_truthy } it "should create new event" do expect { service.merge_mr(merge_request, merge_request.author) }.to change { Event.count } @@ -69,7 +69,7 @@ describe EventCreateService do describe :reopen_mr do let(:merge_request) { create(:merge_request) } - it { service.reopen_mr(merge_request, merge_request.author).should be_true } + it { expect(service.reopen_mr(merge_request, merge_request.author)).to be_truthy } it "should create new event" do expect { service.reopen_mr(merge_request, merge_request.author) }.to change { Event.count } @@ -83,7 +83,7 @@ describe EventCreateService do describe :open_milestone do let(:milestone) { create(:milestone) } - it { service.open_milestone(milestone, user).should be_true } + it { expect(service.open_milestone(milestone, user)).to be_truthy } it "should create new event" do expect { service.open_milestone(milestone, user) }.to change { Event.count } @@ -93,7 +93,7 @@ describe EventCreateService do describe :close_mr do let(:milestone) { create(:milestone) } - it { service.close_milestone(milestone, user).should be_true } + it { expect(service.close_milestone(milestone, user)).to be_truthy } it "should create new event" do expect { service.close_milestone(milestone, user) }.to change { Event.count } diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 3a75d65b5bc..1b1e3ca5f8b 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -20,7 +20,7 @@ describe GitPushService do service.execute(project, user, @blankrev, @newrev, @ref) end - it { should be_true } + it { is_expected.to be_truthy } end context 'existing branch' do @@ -28,7 +28,7 @@ describe GitPushService do service.execute(project, user, @oldrev, @newrev, @ref) end - it { should be_true } + it { is_expected.to be_truthy } end context 'rm branch' do @@ -36,7 +36,7 @@ describe GitPushService do service.execute(project, user, @oldrev, @blankrev, @ref) end - it { should be_true } + it { is_expected.to be_truthy } end end @@ -49,41 +49,54 @@ describe GitPushService do subject { @push_data } - it { should include(before: @oldrev) } - it { should include(after: @newrev) } - it { should include(ref: @ref) } - it { should include(user_id: user.id) } - it { should include(user_name: user.name) } - it { should include(project_id: project.id) } + it { is_expected.to include(object_kind: 'push') } + it { is_expected.to include(before: @oldrev) } + it { is_expected.to include(after: @newrev) } + it { is_expected.to include(ref: @ref) } + it { is_expected.to include(user_id: user.id) } + it { is_expected.to include(user_name: user.name) } + it { is_expected.to include(project_id: project.id) } context "with repository data" do subject { @push_data[:repository] } - it { should include(name: project.name) } - it { should include(url: project.url_to_repo) } - it { should include(description: project.description) } - it { should include(homepage: project.web_url) } + it { is_expected.to include(name: project.name) } + it { is_expected.to include(url: project.url_to_repo) } + it { is_expected.to include(description: project.description) } + it { is_expected.to include(homepage: project.web_url) } end context "with commits" do subject { @push_data[:commits] } - it { should be_an(Array) } - it { should have(1).element } + it { is_expected.to be_an(Array) } + it 'has 1 element' do + expect(subject.size).to eq(1) + end context "the commit" do subject { @push_data[:commits].first } - it { should include(id: @commit.id) } - it { should include(message: @commit.safe_message) } - it { should include(timestamp: @commit.date.xmlschema) } - it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.to_param}/commit/#{@commit.id}") } + it { is_expected.to include(id: @commit.id) } + it { is_expected.to include(message: @commit.safe_message) } + it { is_expected.to include(timestamp: @commit.date.xmlschema) } + it do + is_expected.to include( + url: [ + Gitlab.config.gitlab.url, + project.namespace.to_param, + project.to_param, + 'commit', + @commit.id + ].join('/') + ) + end context "with a author" do subject { @push_data[:commits].first[:author] } - it { should include(name: @commit.author_name) } - it { should include(email: @commit.author_email) } + it { is_expected.to include(name: @commit.author_name) } + it { is_expected.to include(email: @commit.author_email) } end end end @@ -95,46 +108,46 @@ describe GitPushService do @event = Event.last end - it { @event.should_not be_nil } - it { @event.project.should == project } - it { @event.action.should == Event::PUSHED } - it { @event.data.should == service.push_data } + it { expect(@event).not_to be_nil } + it { expect(@event.project).to eq(project) } + it { expect(@event.action).to eq(Event::PUSHED) } + it { expect(@event.data).to eq(service.push_data) } end describe "Web Hooks" do context "execute web hooks" do it "when pushing a branch for the first time" do - project.should_receive(:execute_hooks) - project.default_branch.should == "master" - project.protected_branches.should_receive(:create).with({ name: "master", developers_can_push: false }) + expect(project).to receive(:execute_hooks) + expect(project.default_branch).to eq("master") + expect(project.protected_branches).to receive(:create).with({ name: "master", developers_can_push: false }) service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') end it "when pushing a branch for the first time with default branch protection disabled" do ApplicationSetting.any_instance.stub(default_branch_protection: 0) - project.should_receive(:execute_hooks) - project.default_branch.should == "master" - project.protected_branches.should_not_receive(:create) + expect(project).to receive(:execute_hooks) + expect(project.default_branch).to eq("master") + expect(project.protected_branches).not_to receive(:create) service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') end it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do ApplicationSetting.any_instance.stub(default_branch_protection: 1) - project.should_receive(:execute_hooks) - project.default_branch.should == "master" - project.protected_branches.should_receive(:create).with({ name: "master", developers_can_push: true }) + expect(project).to receive(:execute_hooks) + expect(project.default_branch).to eq("master") + expect(project.protected_branches).to receive(:create).with({ name: "master", developers_can_push: true }) service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') end it "when pushing new commits to existing branch" do - project.should_receive(:execute_hooks) + expect(project).to receive(:execute_hooks) service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master') end it "when pushing tags" do - project.should_not_receive(:execute_hooks) + expect(project).not_to receive(:execute_hooks) service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0') end end @@ -156,7 +169,7 @@ describe GitPushService do end it "creates a note if a pushed commit mentions an issue" do - Note.should_receive(:create_cross_reference_note).with(issue, commit, commit_author, project) + expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author, project) service.execute(project, user, @oldrev, @newrev, @ref) end @@ -164,35 +177,26 @@ describe GitPushService do it "only creates a cross-reference note if one doesn't already exist" do Note.create_cross_reference_note(issue, commit, user, project) - Note.should_not_receive(:create_cross_reference_note).with(issue, commit, commit_author, project) + expect(Note).not_to receive(:create_cross_reference_note).with(issue, commit, commit_author, project) service.execute(project, user, @oldrev, @newrev, @ref) end it "defaults to the pushing user if the commit's author is not known" do commit.stub(author_name: 'unknown name', author_email: 'unknown@email.com') - Note.should_receive(:create_cross_reference_note).with(issue, commit, user, project) + expect(Note).to receive(:create_cross_reference_note).with(issue, commit, user, project) service.execute(project, user, @oldrev, @newrev, @ref) end it "finds references in the first push to a non-default branch" do - project.repository.stub(:commits_between).with(@blankrev, @newrev).and_return([]) - project.repository.stub(:commits_between).with("master", @newrev).and_return([commit]) + allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([]) + allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit]) - Note.should_receive(:create_cross_reference_note).with(issue, commit, commit_author, project) + expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author, project) service.execute(project, user, @blankrev, @newrev, 'refs/heads/other') end - - it "finds references in the first push to a default branch" do - project.repository.stub(:commits_between).with(@blankrev, @newrev).and_return([]) - project.repository.stub(:commits).with(@newrev).and_return([commit]) - - Note.should_receive(:create_cross_reference_note).with(issue, commit, commit_author, project) - - service.execute(project, user, @blankrev, @newrev, 'refs/heads/master') - end end describe "closing issues from pushed commits" do @@ -215,7 +219,7 @@ describe GitPushService do it "closes issues with commit messages" do service.execute(project, user, @oldrev, @newrev, @ref) - Issue.find(issue.id).should be_closed + expect(Issue.find(issue.id)).to be_closed end it "doesn't create cross-reference notes for a closing reference" do @@ -232,7 +236,7 @@ describe GitPushService do service.execute(project, user, @oldrev, @newrev, 'refs/heads/hurf') }.not_to change { Note.where(project_id: project.id, system: true).count } - Issue.find(issue.id).should be_opened + expect(Issue.find(issue.id)).to be_opened end end end diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index e65a8204c54..fcf462edbfc 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -19,27 +19,27 @@ describe GitTagPushService do subject { @push_data } - it { should include(ref: @ref) } - it { should include(before: @oldrev) } - it { should include(after: @newrev) } - it { should include(user_id: user.id) } - it { should include(user_name: user.name) } - it { should include(project_id: project.id) } + it { is_expected.to include(ref: @ref) } + it { is_expected.to include(before: @oldrev) } + it { is_expected.to include(after: @newrev) } + it { is_expected.to include(user_id: user.id) } + it { is_expected.to include(user_name: user.name) } + it { is_expected.to include(project_id: project.id) } context 'With repository data' do subject { @push_data[:repository] } - it { should include(name: project.name) } - it { should include(url: project.url_to_repo) } - it { should include(description: project.description) } - it { should include(homepage: project.web_url) } + it { is_expected.to include(name: project.name) } + it { is_expected.to include(url: project.url_to_repo) } + it { is_expected.to include(description: project.description) } + it { is_expected.to include(homepage: project.web_url) } end end describe "Web Hooks" do context "execute web hooks" do it "when pushing tags" do - project.should_receive(:execute_hooks) + expect(project).to receive(:execute_hooks) service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0') end end diff --git a/spec/services/issues/bulk_update_context_spec.rb b/spec/services/issues/bulk_update_context_spec.rb deleted file mode 100644 index f4c9148f1a3..00000000000 --- a/spec/services/issues/bulk_update_context_spec.rb +++ /dev/null @@ -1,110 +0,0 @@ -require 'spec_helper' - -describe Issues::BulkUpdateService do - let(:issue) { - create(:issue, project: @project) - } - - before do - @user = create :user - opts = { - name: "GitLab", - namespace: @user.namespace - } - @project = Projects::CreateService.new(@user, opts).execute - end - - describe :close_issue do - - before do - @issues = 5.times.collect do - create(:issue, project: @project) - end - @params = { - update: { - status: 'closed', - issues_ids: @issues.map(&:id) - } - } - end - - it { - result = Issues::BulkUpdateService.new(@project, @user, @params).execute - result[:success].should be_true - result[:count].should == @issues.count - - @project.issues.opened.should be_empty - @project.issues.closed.should_not be_empty - } - - end - - describe :reopen_issues do - - before do - @issues = 5.times.collect do - create(:closed_issue, project: @project) - end - @params = { - update: { - status: 'reopen', - issues_ids: @issues.map(&:id) - } - } - end - - it { - result = Issues::BulkUpdateService.new(@project, @user, @params).execute - result[:success].should be_true - result[:count].should == @issues.count - - @project.issues.closed.should be_empty - @project.issues.opened.should_not be_empty - } - - end - - describe :update_assignee do - - before do - @new_assignee = create :user - @params = { - update: { - issues_ids: [issue.id], - assignee_id: @new_assignee.id - } - } - end - - it { - result = Issues::BulkUpdateService.new(@project, @user, @params).execute - result[:success].should be_true - result[:count].should == 1 - - @project.issues.first.assignee.should == @new_assignee - } - - end - - describe :update_milestone do - - before do - @milestone = create :milestone - @params = { - update: { - issues_ids: [issue.id], - milestone_id: @milestone.id - } - } - end - - it { - result = Issues::BulkUpdateService.new(@project, @user, @params).execute - result[:success].should be_true - result[:count].should == 1 - - @project.issues.first.milestone.should == @milestone - } - end - -end diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb new file mode 100644 index 00000000000..a97c55011c9 --- /dev/null +++ b/spec/services/issues/bulk_update_service_spec.rb @@ -0,0 +1,121 @@ +require 'spec_helper' + +describe Issues::BulkUpdateService do + let(:issue) { + create(:issue, project: @project) + } + + before do + @user = create :user + opts = { + name: "GitLab", + namespace: @user.namespace + } + @project = Projects::CreateService.new(@user, opts).execute + end + + describe :close_issue do + + before do + @issues = 5.times.collect do + create(:issue, project: @project) + end + @params = { + state_event: 'close', + issues_ids: @issues.map(&:id) + } + end + + it { + result = Issues::BulkUpdateService.new(@project, @user, @params).execute + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(@issues.count) + + expect(@project.issues.opened).to be_empty + expect(@project.issues.closed).not_to be_empty + } + + end + + describe :reopen_issues do + + before do + @issues = 5.times.collect do + create(:closed_issue, project: @project) + end + @params = { + state_event: 'reopen', + issues_ids: @issues.map(&:id) + } + end + + it { + result = Issues::BulkUpdateService.new(@project, @user, @params).execute + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(@issues.count) + + expect(@project.issues.closed).to be_empty + expect(@project.issues.opened).not_to be_empty + } + + end + + describe :update_assignee do + + before do + @new_assignee = create :user + @params = { + issues_ids: [issue.id], + assignee_id: @new_assignee.id + } + end + + it { + result = Issues::BulkUpdateService.new(@project, @user, @params).execute + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(1) + + expect(@project.issues.first.assignee).to eq(@new_assignee) + } + + it 'allows mass-unassigning' do + @project.issues.first.update_attribute(:assignee, @new_assignee) + expect(@project.issues.first.assignee).not_to be_nil + + @params[:assignee_id] = -1 + + Issues::BulkUpdateService.new(@project, @user, @params).execute + expect(@project.issues.first.assignee).to be_nil + end + + it 'does not unassign when assignee_id is not present' do + @project.issues.first.update_attribute(:assignee, @new_assignee) + expect(@project.issues.first.assignee).not_to be_nil + + @params[:assignee_id] = '' + + Issues::BulkUpdateService.new(@project, @user, @params).execute + expect(@project.issues.first.assignee).not_to be_nil + end + end + + describe :update_milestone do + + before do + @milestone = create :milestone + @params = { + issues_ids: [issue.id], + milestone_id: @milestone.id + } + end + + it { + result = Issues::BulkUpdateService.new(@project, @user, @params).execute + expect(result[:success]).to be_truthy + expect(result[:count]).to eq(1) + + expect(@project.issues.first.milestone).to eq(@milestone) + } + end + +end diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index d4f2cc1339b..d15dff1b52b 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -17,18 +17,18 @@ describe Issues::CloseService do @issue = Issues::CloseService.new(project, user, {}).execute(issue) end - it { @issue.should be_valid } - it { @issue.should be_closed } + it { expect(@issue).to be_valid } + it { expect(@issue).to be_closed } it 'should send email to user2 about assign of new issue' do email = ActionMailer::Base.deliveries.last - email.to.first.should == user2.email - email.subject.should include(issue.title) + expect(email.to.first).to eq(user2.email) + expect(email.subject).to include(issue.title) end it 'should create system note about issue reassign' do note = @issue.notes.last - note.note.should include "Status changed to closed" + expect(note.note).to include "Status changed to closed" end end end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 90720be5ded..7f1ebcb3198 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -16,8 +16,8 @@ describe Issues::CreateService do @issue = Issues::CreateService.new(project, user, opts).execute end - it { @issue.should be_valid } - it { @issue.title.should == 'Awesome issue' } + it { expect(@issue).to be_valid } + it { expect(@issue.title).to eq('Awesome issue') } end end end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 964b3a707e4..22b89bec96d 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -27,27 +27,27 @@ describe Issues::UpdateService do @issue.reload end - it { @issue.should be_valid } - it { @issue.title.should == 'New title' } - it { @issue.assignee.should == user2 } - it { @issue.should be_closed } - it { @issue.labels.count.should == 1 } - it { @issue.labels.first.title.should == 'Bug' } + it { expect(@issue).to be_valid } + it { expect(@issue.title).to eq('New title') } + it { expect(@issue.assignee).to eq(user2) } + it { expect(@issue).to be_closed } + it { expect(@issue.labels.count).to eq(1) } + it { expect(@issue.labels.first.title).to eq('Bug') } it 'should send email to user2 about assign of new issue' do email = ActionMailer::Base.deliveries.last - email.to.first.should == user2.email - email.subject.should include(issue.title) + expect(email.to.first).to eq(user2.email) + expect(email.subject).to include(issue.title) end it 'should create system note about issue reassign' do note = @issue.notes.last - note.note.should include "Reassigned to \@#{user2.username}" + expect(note.note).to include "Reassigned to \@#{user2.username}" end it 'should create system note about issue label edit' do note = @issue.notes[1] - note.note.should include "Added ~#{label.id} label" + expect(note.note).to include "Added ~#{label.id} label" end end end diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index 5060a67bebf..b3cbfd4b5b8 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -16,13 +16,13 @@ describe MergeRequests::CloseService do let(:service) { MergeRequests::CloseService.new(project, user, {}) } before do - service.stub(:execute_hooks) + allow(service).to receive(:execute_hooks) @merge_request = service.execute(merge_request) end - it { @merge_request.should be_valid } - it { @merge_request.should be_closed } + it { expect(@merge_request).to be_valid } + it { expect(@merge_request).to be_closed } it 'should execute hooks with close action' do expect(service).to have_received(:execute_hooks). @@ -31,13 +31,13 @@ describe MergeRequests::CloseService do it 'should send email to user2 about assign of new merge_request' do email = ActionMailer::Base.deliveries.last - email.to.first.should == user2.email - email.subject.should include(merge_request.title) + expect(email.to.first).to eq(user2.email) + expect(email.subject).to include(merge_request.title) end it 'should create system note about merge_request reassign' do note = @merge_request.notes.last - note.note.should include 'Status changed to closed' + expect(note.note).to include 'Status changed to closed' end end end diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index dbd21143690..d9bfdf64308 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -18,13 +18,13 @@ describe MergeRequests::CreateService do before do project.team << [user, :master] - service.stub(:execute_hooks) + allow(service).to receive(:execute_hooks) @merge_request = service.execute end - it { @merge_request.should be_valid } - it { @merge_request.title.should == 'Awesome merge_request' } + it { expect(@merge_request).to be_valid } + it { expect(@merge_request.title).to eq('Awesome merge_request') } it 'should execute hooks with default action' do expect(service).to have_received(:execute_hooks).with(@merge_request) diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 5f61fd3187b..0a25fb12f4e 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -16,13 +16,13 @@ describe MergeRequests::MergeService do let(:service) { MergeRequests::MergeService.new(project, user, {}) } before do - service.stub(:execute_hooks) + allow(service).to receive(:execute_hooks) service.execute(merge_request, 'Awesome message') end - it { merge_request.should be_valid } - it { merge_request.should be_merged } + it { expect(merge_request).to be_valid } + it { expect(merge_request).to be_merged } it 'should execute hooks with merge action' do expect(service).to have_received(:execute_hooks). @@ -31,13 +31,13 @@ describe MergeRequests::MergeService do it 'should send email to user2 about merge of new merge_request' do email = ActionMailer::Base.deliveries.last - email.to.first.should == user2.email - email.subject.should include(merge_request.title) + expect(email.to.first).to eq(user2.email) + expect(email.subject).to include(merge_request.title) end it 'should create system note about merge_request merge' do note = merge_request.notes.last - note.note.should include 'Status changed to merged' + expect(note.note).to include 'Status changed to merged' end end end diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 35c7aac94df..879df0c9c67 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -35,10 +35,10 @@ describe MergeRequests::RefreshService do reload_mrs end - it { @merge_request.notes.should_not be_empty } - it { @merge_request.should be_open } - it { @fork_merge_request.should be_open } - it { @fork_merge_request.notes.should be_empty } + it { expect(@merge_request.notes).not_to be_empty } + it { expect(@merge_request).to be_open } + it { expect(@fork_merge_request).to be_open } + it { expect(@fork_merge_request.notes).to be_empty } end context 'push to origin repo target branch' do @@ -47,10 +47,10 @@ describe MergeRequests::RefreshService do reload_mrs end - it { @merge_request.notes.last.note.should include('changed to merged') } - it { @merge_request.should be_merged } - it { @fork_merge_request.should be_merged } - it { @fork_merge_request.notes.last.note.should include('changed to merged') } + it { expect(@merge_request.notes.last.note).to include('changed to merged') } + it { expect(@merge_request).to be_merged } + it { expect(@fork_merge_request).to be_merged } + it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') } end context 'push to fork repo source branch' do @@ -59,10 +59,10 @@ describe MergeRequests::RefreshService do reload_mrs end - it { @merge_request.notes.should be_empty } - it { @merge_request.should be_open } - it { @fork_merge_request.notes.last.note.should include('new commit') } - it { @fork_merge_request.should be_open } + it { expect(@merge_request.notes).to be_empty } + it { expect(@merge_request).to be_open } + it { expect(@fork_merge_request.notes.last.note).to include('Added 4 commits') } + it { expect(@fork_merge_request).to be_open } end context 'push to fork repo target branch' do @@ -71,10 +71,10 @@ describe MergeRequests::RefreshService do reload_mrs end - it { @merge_request.notes.should be_empty } - it { @merge_request.should be_open } - it { @fork_merge_request.notes.should be_empty } - it { @fork_merge_request.should be_open } + it { expect(@merge_request.notes).to be_empty } + it { expect(@merge_request).to be_open } + it { expect(@fork_merge_request.notes).to be_empty } + it { expect(@fork_merge_request).to be_open } end context 'push to origin repo target branch after fork project was removed' do @@ -84,10 +84,10 @@ describe MergeRequests::RefreshService do reload_mrs end - it { @merge_request.notes.last.note.should include('changed to merged') } - it { @merge_request.should be_merged } - it { @fork_merge_request.should be_open } - it { @fork_merge_request.notes.should be_empty } + it { expect(@merge_request.notes.last.note).to include('changed to merged') } + it { expect(@merge_request).to be_merged } + it { expect(@fork_merge_request).to be_open } + it { expect(@fork_merge_request.notes).to be_empty } end def reload_mrs diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index 2a7066124dc..9401bc3b558 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -16,14 +16,14 @@ describe MergeRequests::ReopenService do let(:service) { MergeRequests::ReopenService.new(project, user, {}) } before do - service.stub(:execute_hooks) + allow(service).to receive(:execute_hooks) merge_request.state = :closed service.execute(merge_request) end - it { merge_request.should be_valid } - it { merge_request.should be_reopened } + it { expect(merge_request).to be_valid } + it { expect(merge_request).to be_reopened } it 'should execute hooks with reopen action' do expect(service).to have_received(:execute_hooks). @@ -32,13 +32,13 @@ describe MergeRequests::ReopenService do it 'should send email to user2 about reopen of merge_request' do email = ActionMailer::Base.deliveries.last - email.to.first.should == user2.email - email.subject.should include(merge_request.title) + expect(email.to.first).to eq(user2.email) + expect(email.subject).to include(merge_request.title) end it 'should create system note about merge_request reopen' do note = merge_request.notes.last - note.note.should include 'Status changed to reopened' + expect(note.note).to include 'Status changed to reopened' end end end diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index b27acb47711..916b01e1c45 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -27,18 +27,18 @@ describe MergeRequests::UpdateService do let(:service) { MergeRequests::UpdateService.new(project, user, opts) } before do - service.stub(:execute_hooks) + allow(service).to receive(:execute_hooks) @merge_request = service.execute(merge_request) @merge_request.reload end - it { @merge_request.should be_valid } - it { @merge_request.title.should == 'New title' } - it { @merge_request.assignee.should == user2 } - it { @merge_request.should be_closed } - it { @merge_request.labels.count.should == 1 } - it { @merge_request.labels.first.title.should == 'Bug' } + it { expect(@merge_request).to be_valid } + it { expect(@merge_request.title).to eq('New title') } + it { expect(@merge_request.assignee).to eq(user2) } + it { expect(@merge_request).to be_closed } + it { expect(@merge_request.labels.count).to eq(1) } + it { expect(@merge_request.labels.first.title).to eq('Bug') } it 'should execute hooks with update action' do expect(service).to have_received(:execute_hooks). @@ -47,18 +47,18 @@ describe MergeRequests::UpdateService do it 'should send email to user2 about assign of new merge_request' do email = ActionMailer::Base.deliveries.last - email.to.first.should == user2.email - email.subject.should include(merge_request.title) + expect(email.to.first).to eq(user2.email) + expect(email.subject).to include(merge_request.title) end it 'should create system note about merge_request reassign' do note = @merge_request.notes.last - note.note.should include "Reassigned to \@#{user2.username}" + expect(note.note).to include "Reassigned to \@#{user2.username}" end it 'should create system note about merge_request label edit' do note = @merge_request.notes[1] - note.note.should include "Added ~#{label.id} label" + expect(note.note).to include "Added ~#{label.id} label" end end end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index f59786efcf9..1a02299bf19 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -18,8 +18,8 @@ describe Notes::CreateService do @note = Notes::CreateService.new(project, user, opts).execute end - it { @note.should be_valid } - it { @note.note.should == 'Awesome comment' } + it { expect(@note).to be_valid } + it { expect(@note.note).to eq('Awesome comment') } end end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 2ba1e3372b9..2074f8e7f78 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -7,10 +7,10 @@ describe NotificationService do describe :new_key do let!(:key) { create(:personal_key) } - it { notification.new_key(key).should be_true } + it { expect(notification.new_key(key)).to be_truthy } it 'should sent email to key owner' do - Notify.should_receive(:new_ssh_key_email).with(key.id) + expect(Notify).to receive(:new_ssh_key_email).with(key.id) notification.new_key(key) end end @@ -20,10 +20,10 @@ describe NotificationService do describe :new_email do let!(:email) { create(:email) } - it { notification.new_email(email).should be_true } + it { expect(notification.new_email(email)).to be_truthy } it 'should send email to email owner' do - Notify.should_receive(:new_email_email).with(email.id) + expect(Notify).to receive(:new_email_email).with(email.id) notification.new_email(email) end end @@ -54,7 +54,7 @@ describe NotificationService do it 'filters out "mentioned in" notes' do mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project) - Notify.should_not_receive(:note_issue_email) + expect(Notify).not_to receive(:note_issue_email) notification.new_note(mentioned_note) end end @@ -87,11 +87,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:note_issue_email).with(user_id, note.id) + expect(Notify).to receive(:note_issue_email).with(user_id, note.id) end def should_not_email(user_id) - Notify.should_not_receive(:note_issue_email).with(user_id, note.id) + expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id) end end @@ -125,17 +125,17 @@ describe NotificationService do it 'filters out "mentioned in" notes' do mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project) - Notify.should_not_receive(:note_issue_email) + expect(Notify).not_to receive(:note_issue_email) notification.new_note(mentioned_note) end end def should_email(user_id) - Notify.should_receive(:note_issue_email).with(user_id, note.id) + expect(Notify).to receive(:note_issue_email).with(user_id, note.id) end def should_not_email(user_id) - Notify.should_not_receive(:note_issue_email).with(user_id, note.id) + expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id) end end @@ -176,11 +176,11 @@ describe NotificationService do end def should_email(user_id, n) - Notify.should_receive(:note_commit_email).with(user_id, n.id) + expect(Notify).to receive(:note_commit_email).with(user_id, n.id) end def should_not_email(user_id, n) - Notify.should_not_receive(:note_commit_email).with(user_id, n.id) + expect(Notify).not_to receive(:note_commit_email).with(user_id, n.id) end end end @@ -211,11 +211,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:new_issue_email).with(user_id, issue.id) + expect(Notify).to receive(:new_issue_email).with(user_id, issue.id) end def should_not_email(user_id) - Notify.should_not_receive(:new_issue_email).with(user_id, issue.id) + expect(Notify).not_to receive(:new_issue_email).with(user_id, issue.id) end end @@ -231,11 +231,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:reassigned_issue_email).with(user_id, issue.id, nil, @u_disabled.id) + expect(Notify).to receive(:reassigned_issue_email).with(user_id, issue.id, nil, @u_disabled.id) end def should_not_email(user_id) - Notify.should_not_receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id) + expect(Notify).not_to receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id) end end @@ -252,11 +252,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) + expect(Notify).to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) end def should_not_email(user_id) - Notify.should_not_receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) + expect(Notify).not_to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) end end @@ -273,11 +273,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) + expect(Notify).to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) end def should_not_email(user_id) - Notify.should_not_receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) + expect(Notify).not_to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) end end end @@ -299,11 +299,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:new_merge_request_email).with(user_id, merge_request.id) + expect(Notify).to receive(:new_merge_request_email).with(user_id, merge_request.id) end def should_not_email(user_id) - Notify.should_not_receive(:new_merge_request_email).with(user_id, merge_request.id) + expect(Notify).not_to receive(:new_merge_request_email).with(user_id, merge_request.id) end end @@ -317,11 +317,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, nil, merge_request.author_id) + expect(Notify).to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, nil, merge_request.author_id) end def should_not_email(user_id) - Notify.should_not_receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id) + expect(Notify).not_to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id) end end @@ -335,11 +335,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + expect(Notify).to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) end def should_not_email(user_id) - Notify.should_not_receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + expect(Notify).not_to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) end end @@ -353,11 +353,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + expect(Notify).to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) end def should_not_email(user_id) - Notify.should_not_receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + expect(Notify).not_to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) end end @@ -371,11 +371,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) + expect(Notify).to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) end def should_not_email(user_id) - Notify.should_not_receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) + expect(Notify).not_to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) end end end @@ -396,11 +396,11 @@ describe NotificationService do end def should_email(user_id) - Notify.should_receive(:project_was_moved_email).with(project.id, user_id) + expect(Notify).to receive(:project_was_moved_email).with(project.id, user_id) end def should_not_email(user_id) - Notify.should_not_receive(:project_was_moved_email).with(project.id, user_id) + expect(Notify).not_to receive(:project_was_moved_email).with(project.id, user_id) end end end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 9c97dad2ff0..8bb48346202 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -16,9 +16,9 @@ describe Projects::CreateService do @project = create_project(@user, @opts) end - it { @project.should be_valid } - it { @project.owner.should == @user } - it { @project.namespace.should == @user.namespace } + it { expect(@project).to be_valid } + it { expect(@project.owner).to eq(@user) } + it { expect(@project.namespace).to eq(@user.namespace) } end context 'group namespace' do @@ -30,9 +30,9 @@ describe Projects::CreateService do @project = create_project(@user, @opts) end - it { @project.should be_valid } - it { @project.owner.should == @group } - it { @project.namespace.should == @group } + it { expect(@project).to be_valid } + it { expect(@project.owner).to eq(@group) } + it { expect(@project.namespace).to eq(@group) } end context 'wiki_enabled creates repository directory' do @@ -42,7 +42,7 @@ describe Projects::CreateService do @path = ProjectWiki.new(@project, @user).send(:path_to_repo) end - it { File.exists?(@path).should be_true } + it { expect(File.exists?(@path)).to be_truthy } end context 'wiki_enabled false does not create wiki repository directory' do @@ -52,7 +52,7 @@ describe Projects::CreateService do @path = ProjectWiki.new(@project, @user).send(:path_to_repo) end - it { File.exists?(@path).should be_false } + it { expect(File.exists?(@path)).to be_falsey } end end end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 5c80345c2b3..e55a2e3f8a0 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -16,18 +16,18 @@ describe Projects::ForkService do describe "successfully creates project in the user namespace" do let(:to_project) { fork_project(@from_project, @to_user) } - it { to_project.owner.should == @to_user } - it { to_project.namespace.should == @to_user.namespace } - it { to_project.star_count.should be_zero } - it { to_project.description.should == @from_project.description } + it { expect(to_project.owner).to eq(@to_user) } + it { expect(to_project.namespace).to eq(@to_user.namespace) } + it { expect(to_project.star_count).to be_zero } + it { expect(to_project.description).to eq(@from_project.description) } end end context 'fork project failure' do it "fails due to transaction failure" do @to_project = fork_project(@from_project, @to_user, false) - @to_project.errors.should_not be_empty - @to_project.errors[:base].should include("Fork transaction failed.") + expect(@to_project.errors).not_to be_empty + expect(@to_project.errors[:base]).to include("Fork transaction failed.") end end @@ -35,9 +35,9 @@ describe Projects::ForkService do it "should fail due to validation, not transaction failure" do @existing_project = create(:project, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace) @to_project = fork_project(@from_project, @to_user) - @existing_project.persisted?.should be_true - @to_project.errors[:base].should include("Invalid fork destination") - @to_project.errors[:base].should_not include("Fork transaction failed.") + expect(@existing_project.persisted?).to be_truthy + expect(@to_project.errors[:base]).to include("Invalid fork destination") + expect(@to_project.errors[:base]).not_to include("Fork transaction failed.") end end end @@ -58,19 +58,19 @@ describe Projects::ForkService do context 'fork project for group' do it 'group owner successfully forks project into the group' do to_project = fork_project(@project, @group_owner, true, @opts) - to_project.owner.should == @group - to_project.namespace.should == @group - to_project.name.should == @project.name - to_project.path.should == @project.path - to_project.description.should == @project.description - to_project.star_count.should be_zero + expect(to_project.owner).to eq(@group) + expect(to_project.namespace).to eq(@group) + expect(to_project.name).to eq(@project.name) + expect(to_project.path).to eq(@project.path) + expect(to_project.description).to eq(@project.description) + expect(to_project.star_count).to be_zero end end context 'fork project for group when user not owner' do it 'group developer should fail to fork project into the group' do to_project = fork_project(@project, @developer, true, @opts) - to_project.errors[:namespace].should == ['insufficient access rights'] + expect(to_project.errors[:namespace]).to eq(['insufficient access rights']) end end @@ -79,10 +79,10 @@ describe Projects::ForkService do existing_project = create(:project, name: @project.name, namespace: @group) to_project = fork_project(@project, @group_owner, true, @opts) - existing_project.persisted?.should be_true - to_project.errors[:base].should == ['Invalid fork destination'] - to_project.errors[:name].should == ['has already been taken'] - to_project.errors[:path].should == ['has already been taken'] + expect(existing_project.persisted?).to be_truthy + expect(to_project.errors[:base]).to eq(['Invalid fork destination']) + expect(to_project.errors[:name]).to eq(['has already been taken']) + expect(to_project.errors[:path]).to eq(['has already been taken']) end end end diff --git a/spec/services/projects/image_service_spec.rb b/spec/services/projects/image_service_spec.rb deleted file mode 100644 index 23c4e227ae3..00000000000 --- a/spec/services/projects/image_service_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -require 'spec_helper' - -describe Projects::ImageService do - describe 'Image service' do - before do - @user = create :user - @project = create :project, creator_id: @user.id, namespace: @user.namespace - end - - context 'for valid gif file' do - before do - gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') - @link_to_image = upload_image(@project.repository, { 'markdown_img' => gif }, "http://test.example/") - end - - it { expect(@link_to_image).to have_key("alt") } - it { expect(@link_to_image).to have_key("url") } - it { expect(@link_to_image).to have_value("banana_sample") } - it { expect(@link_to_image["url"]).to match("http://test.example/uploads/#{@project.path_with_namespace}") } - it { expect(@link_to_image["url"]).to match("banana_sample.gif") } - end - - context 'for valid png file' do - before do - png = fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png') - @link_to_image = upload_image(@project.repository, { 'markdown_img' => png }, "http://test.example/") - end - - it { expect(@link_to_image).to have_key("alt") } - it { expect(@link_to_image).to have_key("url") } - it { expect(@link_to_image).to have_value("dk") } - it { expect(@link_to_image["url"]).to match("http://test.example/uploads/#{@project.path_with_namespace}") } - it { expect(@link_to_image["url"]).to match("dk.png") } - end - - context 'for valid jpg file' do - before do - jpg = fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') - @link_to_image = upload_image(@project.repository, { 'markdown_img' => jpg }, "http://test.example/") - end - - it { expect(@link_to_image).to have_key("alt") } - it { expect(@link_to_image).to have_key("url") } - it { expect(@link_to_image).to have_value("rails_sample") } - it { expect(@link_to_image["url"]).to match("http://test.example/uploads/#{@project.path_with_namespace}") } - it { expect(@link_to_image["url"]).to match("rails_sample.jpg") } - end - - context 'for txt file' do - before do - txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') - @link_to_image = upload_image(@project.repository, { 'markdown_img' => txt }, "http://test.example/") - end - - it { expect(@link_to_image).to be_nil } - end - end - - def upload_image(repository, params, root_url) - Projects::ImageService.new(repository, params, root_url).execute - end -end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 79d0526ff89..5650626fb18 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -8,31 +8,31 @@ describe Projects::TransferService do context 'namespace -> namespace' do before do group.add_owner(user) - @result = transfer_project(project, user, namespace_id: group.id) + @result = transfer_project(project, user, new_namespace_id: group.id) end - it { @result.should be_true } - it { project.namespace.should == group } + it { expect(@result).to be_truthy } + it { expect(project.namespace).to eq(group) } end context 'namespace -> no namespace' do before do - @result = transfer_project(project, user, namespace_id: nil) + @result = transfer_project(project, user, new_namespace_id: nil) end - it { @result.should_not be_nil } # { result.should be_false } passes on nil - it { @result.should be_false } - it { project.namespace.should == user.namespace } + it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil + it { expect(@result).to be_falsey } + it { expect(project.namespace).to eq(user.namespace) } end context 'namespace -> not allowed namespace' do before do - @result = transfer_project(project, user, namespace_id: group.id) + @result = transfer_project(project, user, new_namespace_id: group.id) end - it { @result.should_not be_nil } # { result.should be_false } passes on nil - it { @result.should be_false } - it { project.namespace.should == user.namespace } + it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil + it { expect(@result).to be_falsey } + it { expect(project.namespace).to eq(user.namespace) } end def transfer_project(project, user, params) diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 5a10174eb36..10dbc548e86 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -17,8 +17,8 @@ describe Projects::UpdateService do update_project(@project, @user, @opts) end - it { @created_private.should be_true } - it { @project.private?.should be_true } + it { expect(@created_private).to be_truthy } + it { expect(@project.private?).to be_truthy } end context 'should be internal when updated to internal' do @@ -29,8 +29,8 @@ describe Projects::UpdateService do update_project(@project, @user, @opts) end - it { @created_private.should be_true } - it { @project.internal?.should be_true } + it { expect(@created_private).to be_truthy } + it { expect(@project.internal?).to be_truthy } end context 'should be public when updated to public' do @@ -41,14 +41,14 @@ describe Projects::UpdateService do update_project(@project, @user, @opts) end - it { @created_private.should be_true } - it { @project.public?.should be_true } + it { expect(@created_private).to be_truthy } + it { expect(@project.public?).to be_truthy } end context 'respect configured visibility restrictions setting' do before(:each) do @restrictions = double("restrictions") - @restrictions.stub(:restricted_visibility_levels) { [ "public" ] } + allow(@restrictions).to receive(:restricted_visibility_levels) { [ "public" ] } Settings.stub_chain(:gitlab).and_return(@restrictions) end @@ -60,8 +60,8 @@ describe Projects::UpdateService do update_project(@project, @user, @opts) end - it { @created_private.should be_true } - it { @project.private?.should be_true } + it { expect(@created_private).to be_truthy } + it { expect(@project.private?).to be_truthy } end context 'should be internal when updated to internal' do @@ -72,8 +72,8 @@ describe Projects::UpdateService do update_project(@project, @user, @opts) end - it { @created_private.should be_true } - it { @project.internal?.should be_true } + it { expect(@created_private).to be_truthy } + it { expect(@project.internal?).to be_truthy } end context 'should be private when updated to public' do @@ -84,8 +84,8 @@ describe Projects::UpdateService do update_project(@project, @user, @opts) end - it { @created_private.should be_true } - it { @project.private?.should be_true } + it { expect(@created_private).to be_truthy } + it { expect(@project.private?).to be_truthy } end context 'should be public when updated to public by admin' do @@ -96,8 +96,8 @@ describe Projects::UpdateService do update_project(@project, @admin, @opts) end - it { @created_private.should be_true } - it { @project.public?.should be_true } + it { expect(@created_private).to be_truthy } + it { expect(@project.public?).to be_truthy } end end end diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb new file mode 100644 index 00000000000..fc34b456482 --- /dev/null +++ b/spec/services/projects/upload_service_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe Projects::UploadService do + describe 'File service' do + before do + @user = create :user + @project = create :project, creator_id: @user.id, namespace: @user.namespace + end + + context 'for valid gif file' do + before do + gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + @link_to_file = upload_file(@project.repository, gif) + end + + it { expect(@link_to_file).to have_key('alt') } + it { expect(@link_to_file).to have_key('url') } + it { expect(@link_to_file).to have_key('is_image') } + it { expect(@link_to_file).to have_value('banana_sample') } + it { expect(@link_to_file['is_image']).to equal(true) } + it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } + it { expect(@link_to_file['url']).to match('banana_sample.gif') } + end + + context 'for valid png file' do + before do + png = fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', + 'image/png') + @link_to_file = upload_file(@project.repository, png) + end + + it { expect(@link_to_file).to have_key('alt') } + it { expect(@link_to_file).to have_key('url') } + it { expect(@link_to_file).to have_value('dk') } + it { expect(@link_to_file).to have_key('is_image') } + it { expect(@link_to_file['is_image']).to equal(true) } + it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } + it { expect(@link_to_file['url']).to match('dk.png') } + end + + context 'for valid jpg file' do + before do + jpg = fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') + @link_to_file = upload_file(@project.repository, jpg) + end + + it { expect(@link_to_file).to have_key('alt') } + it { expect(@link_to_file).to have_key('url') } + it { expect(@link_to_file).to have_key('is_image') } + it { expect(@link_to_file).to have_value('rails_sample') } + it { expect(@link_to_file['is_image']).to equal(true) } + it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } + it { expect(@link_to_file['url']).to match('rails_sample.jpg') } + end + + context 'for txt file' do + before do + txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') + @link_to_file = upload_file(@project.repository, txt) + end + + it { expect(@link_to_file).to have_key('alt') } + it { expect(@link_to_file).to have_key('url') } + it { expect(@link_to_file).to have_key('is_image') } + it { expect(@link_to_file).to have_value('doc_sample.txt') } + it { expect(@link_to_file['is_image']).to equal(false) } + it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } + it { expect(@link_to_file['url']).to match('doc_sample.txt') } + end + end + + def upload_file(repository, file) + Projects::UploadService.new(repository, file).execute + end +end diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 3217c571e67..f57bfaea879 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -19,7 +19,7 @@ describe 'Search::GlobalService' do it 'should return public projects only' do context = Search::GlobalService.new(nil, search: "searchable") results = context.execute - results.objects('projects').should match_array [public_project] + expect(results.objects('projects')).to match_array [public_project] end end @@ -27,19 +27,19 @@ describe 'Search::GlobalService' do it 'should return public, internal and private projects' do context = Search::GlobalService.new(user, search: "searchable") results = context.execute - results.objects('projects').should match_array [public_project, found_project, internal_project] + expect(results.objects('projects')).to match_array [public_project, found_project, internal_project] end it 'should return only public & internal projects' do context = Search::GlobalService.new(internal_user, search: "searchable") results = context.execute - results.objects('projects').should match_array [internal_project, public_project] + expect(results.objects('projects')).to match_array [internal_project, public_project] end it 'namespace name should be searchable' do context = Search::GlobalService.new(user, search: found_project.namespace.path) results = context.execute - results.objects('projects').should match_array [found_project] + expect(results.objects('projects')).to match_array [found_project] end end end diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index a45e9d0575c..199ac996608 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -9,35 +9,35 @@ describe SystemHooksService do let (:group_member) { create(:group_member) } context 'event data' do - it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) } - it { event_data(user, :destroy).should include(:event_name, :name, :created_at, :email, :user_id) } - it { event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { event_data(project, :destroy).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { event_data(project_member, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } - it { event_data(project_member, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } - it { event_data(key, :create).should include(:username, :key, :id) } - it { event_data(key, :destroy).should include(:username, :key, :id) } + it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :email, :user_id) } + it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :email, :user_id) } + it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } + it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } + it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(key, :create)).to include(:username, :key, :id) } + it { expect(event_data(key, :destroy)).to include(:username, :key, :id) } it do - event_data(group, :create).should include( + expect(event_data(group, :create)).to include( :event_name, :name, :created_at, :path, :group_id, :owner_name, :owner_email ) end it do - event_data(group, :destroy).should include( + expect(event_data(group, :destroy)).to include( :event_name, :name, :created_at, :path, :group_id, :owner_name, :owner_email ) end it do - event_data(group_member, :create).should include( + expect(event_data(group_member, :create)).to include( :event_name, :created_at, :group_name, :group_path, :group_id, :user_id, :user_name, :user_email, :group_access ) end it do - event_data(group_member, :destroy).should include( + expect(event_data(group_member, :destroy)).to include( :event_name, :created_at, :group_name, :group_path, :group_id, :user_id, :user_name, :user_email, :group_access ) @@ -45,18 +45,18 @@ describe SystemHooksService do end context 'event names' do - it { event_name(user, :create).should eq "user_create" } - it { event_name(user, :destroy).should eq "user_destroy" } - it { event_name(project, :create).should eq "project_create" } - it { event_name(project, :destroy).should eq "project_destroy" } - it { event_name(project_member, :create).should eq "user_add_to_team" } - it { event_name(project_member, :destroy).should eq "user_remove_from_team" } - it { event_name(key, :create).should eq 'key_create' } - it { event_name(key, :destroy).should eq 'key_destroy' } - it { event_name(group, :create).should eq 'group_create' } - it { event_name(group, :destroy).should eq 'group_destroy' } - it { event_name(group_member, :create).should eq 'user_add_to_group' } - it { event_name(group_member, :destroy).should eq 'user_remove_from_group' } + it { expect(event_name(user, :create)).to eq "user_create" } + it { expect(event_name(user, :destroy)).to eq "user_destroy" } + it { expect(event_name(project, :create)).to eq "project_create" } + it { expect(event_name(project, :destroy)).to eq "project_destroy" } + it { expect(event_name(project_member, :create)).to eq "user_add_to_team" } + it { expect(event_name(project_member, :destroy)).to eq "user_remove_from_team" } + it { expect(event_name(key, :create)).to eq 'key_create' } + it { expect(event_name(key, :destroy)).to eq 'key_destroy' } + it { expect(event_name(group, :create)).to eq 'group_create' } + it { expect(event_name(group, :destroy)).to eq 'group_destroy' } + it { expect(event_name(group_member, :create)).to eq 'user_add_to_group' } + it { expect(event_name(group_member, :destroy)).to eq 'user_remove_from_group' } end def event_data(*args) diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb index 76af5bf7b88..d2b505f55a2 100644 --- a/spec/services/test_hook_service_spec.rb +++ b/spec/services/test_hook_service_spec.rb @@ -8,7 +8,7 @@ describe TestHookService do describe :execute do it "should execute successfully" do stub_request(:post, hook.url).to_return(status: 200) - TestHookService.new.execute(hook, user).should be_true + expect(TestHookService.new.execute(hook, user)).to be_truthy end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 773de6628b1..eaec2198dc8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -37,6 +37,8 @@ RSpec.configure do |config| config.include Devise::TestHelpers, type: :controller config.include TestEnv + config.infer_spec_type_from_file_location! + config.raise_errors_for_deprecations! config.before(:suite) do TestEnv.init diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index d2d532d9738..cca7652093a 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -36,4 +36,15 @@ RSpec.configure do |config| config.after(:each) do DatabaseCleaner.clean end + + # rspec-rails 3 will no longer automatically infer an example group's spec type + # from the file location. You can explicitly opt-in to the feature using this + # config option. + # To explicitly tag specs without using automatic inference, set the `:type` + # metadata manually: + # + # describe ThingsController, :type => :controller do + # # Equivalent to being in spec/controllers + # end + config.infer_spec_type_from_file_location! end diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index ebd74206699..305592fa5a6 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -39,7 +39,7 @@ def common_mentionable_setup # unrecognized commits. commitmap = { '1234567890a' => mentioned_commit } extra_commits.each { |c| commitmap[c.short_id] = c } - mproject.repository.stub(:commit) { |sha| commitmap[sha] } + allow(mproject.repository).to receive(:commit) { |sha| commitmap[sha] } set_mentionable_text.call(ref_string) end end @@ -48,19 +48,19 @@ shared_examples 'a mentionable' do common_mentionable_setup it 'generates a descriptive back-reference' do - subject.gfm_reference.should == backref_text + expect(subject.gfm_reference).to eq(backref_text) end it "extracts references from its reference property" do # De-duplicate and omit itself refs = subject.references(mproject) - refs.should have(6).items - refs.should include(mentioned_issue) - refs.should include(mentioned_mr) - refs.should include(mentioned_commit) - refs.should include(ext_issue) - refs.should include(ext_mr) - refs.should include(ext_commit) + expect(refs.size).to eq(6) + expect(refs).to include(mentioned_issue) + expect(refs).to include(mentioned_mr) + expect(refs).to include(mentioned_commit) + expect(refs).to include(ext_issue) + expect(refs).to include(ext_mr) + expect(refs).to include(ext_commit) end it 'creates cross-reference notes' do @@ -68,7 +68,7 @@ shared_examples 'a mentionable' do ext_issue, ext_mr, ext_commit] mentioned_objects.each do |referenced| - Note.should_receive(:create_cross_reference_note).with(referenced, subject.local_reference, mauthor, mproject) + expect(Note).to receive(:create_cross_reference_note).with(referenced, subject.local_reference, mauthor, mproject) end subject.create_cross_references!(mproject, mauthor) @@ -77,8 +77,8 @@ shared_examples 'a mentionable' do it 'detects existing cross-references' do Note.create_cross_reference_note(mentioned_issue, subject.local_reference, mauthor, mproject) - subject.has_mentioned?(mentioned_issue).should be_true - subject.has_mentioned?(mentioned_mr).should be_false + expect(subject.has_mentioned?(mentioned_issue)).to be_truthy + expect(subject.has_mentioned?(mentioned_mr)).to be_falsey end end @@ -95,12 +95,12 @@ shared_examples 'an editable mentionable' do "#{ext_proj.path_with_namespace}##{other_ext_issue.iid}" [mentioned_issue, mentioned_commit, ext_issue].each do |oldref| - Note.should_not_receive(:create_cross_reference_note).with(oldref, subject.local_reference, + expect(Note).not_to receive(:create_cross_reference_note).with(oldref, subject.local_reference, mauthor, mproject) end [other_issue, other_ext_issue].each do |newref| - Note.should_receive(:create_cross_reference_note).with( + expect(Note).to receive(:create_cross_reference_note).with( newref, subject.local_reference, mauthor, diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb index 4c4775da692..aadf791bf3f 100644 --- a/spec/support/repo_helpers.rb +++ b/spec/support/repo_helpers.rb @@ -43,6 +43,25 @@ eos ) end + def another_sample_commit + OpenStruct.new( + id: "e56497bb5f03a90a51293fc6d516788730953899", + parent_id: '4cd80ccab63c82b4bad16faa5193fbd2aa06df40', + author_full_name: "Sytse Sijbrandij", + author_email: "sytse@gitlab.com", + files_changed_count: 1, + message: <<eos +Add directory structure for tree_helper spec + +This directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module + +See [merge request #275](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/275#note_732774) + +See merge request !2 +eos + ) + end + def sample_big_commit OpenStruct.new( id: "913c66a37b4a45b9769037c55c2d238bd0942d2e", diff --git a/spec/support/taskable_shared_examples.rb b/spec/support/taskable_shared_examples.rb index 42252675683..490f453d468 100644 --- a/spec/support/taskable_shared_examples.rb +++ b/spec/support/taskable_shared_examples.rb @@ -34,9 +34,9 @@ EOT end it 'knows if it has tasks' do - expect(subject.tasks?).to be_true + expect(subject.tasks?).to be_truthy subject.description = 'Now I have no tasks' - expect(subject.tasks?).to be_false + expect(subject.tasks?).to be_falsey end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 24fee7c0379..f869488d8d8 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -19,21 +19,10 @@ module TestEnv # See gitlab.yml.example test section for paths # def init(opts = {}) - RSpec::Mocks::setup(self) - # Disable mailer for spinach tests disable_mailer if opts[:mailer] == false - # Clean /tmp/tests - tmp_test_path = Rails.root.join('tmp', 'tests') - - if File.directory?(tmp_test_path) - Dir.entries(tmp_test_path).each do |entry| - unless ['.', '..', 'gitlab-shell', factory_repo_name].include?(entry) - FileUtils.rm_r(File.join(tmp_test_path, entry)) - end - end - end + clean_test_path FileUtils.mkdir_p(repos_path) @@ -49,18 +38,33 @@ module TestEnv end def enable_mailer - NotificationService.any_instance.unstub(:mailer) + allow_any_instance_of(NotificationService).to receive(:mailer).and_call_original + end + + # Clean /tmp/tests + # + # Keeps gitlab-shell and gitlab-test + def clean_test_path + tmp_test_path = Rails.root.join('tmp', 'tests', '**') + + Dir[tmp_test_path].each do |entry| + unless File.basename(entry) =~ /\Agitlab-(shell|test)\z/ + FileUtils.rm_rf(entry) + end + end end def setup_gitlab_shell - `rake gitlab:shell:install` + unless File.directory?(Rails.root.join(*%w(tmp tests gitlab-shell))) + `rake gitlab:shell:install` + end end def setup_factory_repo clone_url = "https://gitlab.com/gitlab-org/#{factory_repo_name}.git" unless File.directory?(factory_repo_path) - system(*%W(git clone #{clone_url} #{factory_repo_path})) + system(*%W(git clone -q #{clone_url} #{factory_repo_path})) end Dir.chdir(factory_repo_path) do @@ -81,7 +85,7 @@ module TestEnv end # We must copy bare repositories because we will push to them. - system(*%W(git clone --bare #{factory_repo_path} #{factory_repo_path_bare})) + system(*%W(git clone -q --bare #{factory_repo_path} #{factory_repo_path_bare})) end def copy_repo(project) @@ -103,7 +107,7 @@ module TestEnv end def factory_repo_path_bare - factory_repo_path.to_s + '_bare' + "#{factory_repo_path}_bare" end def factory_repo_name diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 71a45eb2fa6..60942cc95fc 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -13,7 +13,7 @@ describe 'gitlab:app namespace rake task' do describe 'backup_restore' do before do # avoid writing task output to spec progress - $stdout.stub :write + allow($stdout).to receive :write end let :run_rake_task do @@ -24,7 +24,7 @@ describe 'gitlab:app namespace rake task' do context 'gitlab version' do before do Dir.stub glob: [] - Dir.stub :chdir + allow(Dir).to receive :chdir File.stub exists?: true Kernel.stub system: true FileUtils.stub cp_r: true @@ -41,9 +41,9 @@ describe 'gitlab:app namespace rake task' do it 'should invoke restoration on mach' do YAML.stub load_file: {gitlab_version: gitlab_version} - Rake::Task["gitlab:backup:db:restore"].should_receive :invoke - Rake::Task["gitlab:backup:repo:restore"].should_receive :invoke - Rake::Task["gitlab:shell:setup"].should_receive :invoke + expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke + expect(Rake::Task["gitlab:backup:repo:restore"]).to receive :invoke + expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke expect { run_rake_task }.to_not raise_error end end diff --git a/spec/tasks/gitlab/mail_google_schema_whitelisting.rb b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb index 45aaf0fc90b..22e746870dc 100644 --- a/spec/tasks/gitlab/mail_google_schema_whitelisting.rb +++ b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb @@ -12,7 +12,7 @@ describe 'gitlab:mail_google_schema_whitelisting rake task' do describe 'call' do before do # avoid writing task output to spec progress - $stdout.stub :write + allow($stdout).to receive :write end let :run_rake_task do diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 4273fd1019a..8eabc46112b 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe PostReceive do context "as a resque worker" do it "reponds to #perform" do - PostReceive.new.should respond_to(:perform) + expect(PostReceive.new).to respond_to(:perform) end end @@ -13,23 +13,23 @@ describe PostReceive do let(:key_id) { key.shell_id } it "fetches the correct project" do - Project.should_receive(:find_with_namespace).with(project.path_with_namespace).and_return(project) + expect(Project).to receive(:find_with_namespace).with(project.path_with_namespace).and_return(project) PostReceive.new.perform(pwd(project), key_id, changes) end it "does not run if the author is not in the project" do - Key.stub(:find_by).with(hash_including(id: anything())) { nil } + allow(Key).to receive(:find_by).with(hash_including(id: anything())) { nil } - project.should_not_receive(:execute_hooks) + expect(project).not_to receive(:execute_hooks) - PostReceive.new.perform(pwd(project), key_id, changes).should be_false + expect(PostReceive.new.perform(pwd(project), key_id, changes)).to be_falsey end it "asks the project to trigger all hooks" do Project.stub(find_with_namespace: project) - project.should_receive(:execute_hooks) - project.should_receive(:execute_services) - project.should_receive(:update_merge_requests) + expect(project).to receive(:execute_hooks) + expect(project).to receive(:execute_services) + expect(project).to receive(:update_merge_requests) PostReceive.new.perform(pwd(project), key_id, changes) end diff --git a/vendor/assets/javascripts/jquery.sticky-kit.min.js b/vendor/assets/javascripts/jquery.sticky-kit.min.js new file mode 100644 index 00000000000..e8bb207c5a5 --- /dev/null +++ b/vendor/assets/javascripts/jquery.sticky-kit.min.js @@ -0,0 +1,9 @@ +/* + Sticky-kit v1.1.1 | WTFPL | Leaf Corcoran 2014 | http://leafo.net +*/ +(function(){var k,e;k=this.jQuery||window.jQuery;e=k(window);k.fn.stick_in_parent=function(d){var v,y,n,p,h,C,s,G,q,H;null==d&&(d={});s=d.sticky_class;y=d.inner_scrolling;C=d.recalc_every;h=d.parent;p=d.offset_top;n=d.spacer;v=d.bottoming;null==p&&(p=0);null==h&&(h=void 0);null==y&&(y=!0);null==s&&(s="is_stuck");null==v&&(v=!0);G=function(a,d,q,z,D,t,r,E){var u,F,m,A,c,f,B,w,x,g,b;if(!a.data("sticky_kit")){a.data("sticky_kit",!0);f=a.parent();null!=h&&(f=f.closest(h));if(!f.length)throw"failed to find stick parent"; +u=m=!1;(g=null!=n?n&&a.closest(n):k("<div />"))&&g.css("position",a.css("position"));B=function(){var c,e,l;if(!E&&(c=parseInt(f.css("border-top-width"),10),e=parseInt(f.css("padding-top"),10),d=parseInt(f.css("padding-bottom"),10),q=f.offset().top+c+e,z=f.height(),m&&(u=m=!1,null==n&&(a.insertAfter(g),g.detach()),a.css({position:"",top:"",width:"",bottom:""}).removeClass(s),l=!0),D=a.offset().top-parseInt(a.css("margin-top"),10)-p,t=a.outerHeight(!0),r=a.css("float"),g&&g.css({width:a.outerWidth(!0), +height:t,display:a.css("display"),"vertical-align":a.css("vertical-align"),"float":r}),l))return b()};B();if(t!==z)return A=void 0,c=p,x=C,b=function(){var b,k,l,h;if(!E&&(null!=x&&(--x,0>=x&&(x=C,B())),l=e.scrollTop(),null!=A&&(k=l-A),A=l,m?(v&&(h=l+t+c>z+q,u&&!h&&(u=!1,a.css({position:"fixed",bottom:"",top:c}).trigger("sticky_kit:unbottom"))),l<D&&(m=!1,c=p,null==n&&("left"!==r&&"right"!==r||a.insertAfter(g),g.detach()),b={position:"",width:"",top:""},a.css(b).removeClass(s).trigger("sticky_kit:unstick")), +y&&(b=e.height(),t+p>b&&!u&&(c-=k,c=Math.max(b-t,c),c=Math.min(p,c),m&&a.css({top:c+"px"})))):l>D&&(m=!0,b={position:"fixed",top:c},b.width="border-box"===a.css("box-sizing")?a.outerWidth()+"px":a.width()+"px",a.css(b).addClass(s),null==n&&(a.after(g),"left"!==r&&"right"!==r||g.append(a)),a.trigger("sticky_kit:stick")),m&&v&&(null==h&&(h=l+t+c>z+q),!u&&h)))return u=!0,"static"===f.css("position")&&f.css({position:"relative"}),a.css({position:"absolute",bottom:d,top:"auto"}).trigger("sticky_kit:bottom")}, +w=function(){B();return b()},F=function(){E=!0;e.off("touchmove",b);e.off("scroll",b);e.off("resize",w);k(document.body).off("sticky_kit:recalc",w);a.off("sticky_kit:detach",F);a.removeData("sticky_kit");a.css({position:"",bottom:"",top:"",width:""});f.position("position","");if(m)return null==n&&("left"!==r&&"right"!==r||a.insertAfter(g),g.remove()),a.removeClass(s)},e.on("touchmove",b),e.on("scroll",b),e.on("resize",w),k(document.body).on("sticky_kit:recalc",w),a.on("sticky_kit:detach",F),setTimeout(b, +0)}};q=0;for(H=this.length;q<H;q++)d=this[q],G(k(d));return this}}).call(this); |