diff options
146 files changed, 1416 insertions, 442 deletions
diff --git a/CHANGELOG b/CHANGELOG index ade877feb9a..df598502820 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,30 @@ Please view this file on the master branch, on stable branches it's out of date. -v 7.11.0 (unreleased) +v 7.12.0 (unreleased) + - Refactor permission checks with issues and merge requests project settings (Stan Hu) + - Fix Markdown preview not working in Edit Milestone page (Stan Hu) + - Fix Zen Mode not closing with ESC key (Stan Hu) + - Add web hook support for note events (Stan Hu) + - Disable "New Issue" and "New Merge Request" buttons when features are disabled in project settings (Stan Hu) + - Remove Rack Attack monkey patches and bump to version 4.3.0 (Stan Hu) + - Allow to configure location of the `.gitlab_shell_secret` file. (Jakub Jirutka) + - Disabled expansion of top/bottom blobs for new file diffs + - Update Asciidoctor gem to version 1.5.2. (Jakub Jirutka) + - Fix resolving of relative links to repository files in AsciiDoc documents. (Jakub Jirutka) + - Use the user list from the target project in a merge request (Stan Hu) + - Default extention for wiki pages is now .md instead of .markdown (Jeroen van Baarsen) + - Add validation to wiki page creation (only [a-zA-Z0-9/_-] are allowed) (Jeroen van Baarsen) + - Fix new/empty milestones showing 100% completion value (Jonah Bishop) + +v 7.11.2 + - no changes + +v 7.11.1 + - no changes + +v 7.11.0 + - Fall back to Plaintext when Syntaxhighlighting doesn't work. Fixes some buggy lexers (Hannes Rosenögger) + - Get editing comments to work in Chrome 43 again. - Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu) - Don't show duplicate deploy keys - Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger) @@ -11,8 +35,6 @@ v 7.11.0 (unreleased) - Don't allow a merge request to be merged when its title starts with "WIP". - Add a page title to every page. - Allow primary email to be set to an email that you've already added. - - Fix Error 500 when searching Wiki pages (Stan Hu) - - Get Gitorious importer to work again. - Fix clone URL field and X11 Primary selection (Dmitry Medvinsky) - Ignore invalid lines in .gitmodules - Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu) @@ -21,7 +43,6 @@ v 7.11.0 (unreleased) - Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu) - Improve project page UI - Fix broken file browsing with relative submodule in personal projects (Stan Hu) - - Fix DB error when trying to tag a repository (Stan Hu) - Add "Reply quoting selected text" shortcut key (`r`) - Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention. - Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention. @@ -32,20 +53,19 @@ v 7.11.0 (unreleased) - Show Atom feed buttons everywhere where applicable. - Add project activity atom feed. - Don't crash when an MR from a fork has a cross-reference comment from the target project on one of its commits. + - Explain how to get a new password reset token in welcome emails - Include commit comments in MR from a forked project. - - Fix adding new group members from admin area - Group milestones by title in the dashboard and all other issue views. - Query issues, merge requests and milestones with their IID through API (Julien Bianchi) - Add default project and snippet visibility settings to the admin web UI. - Show incompatible projects in Google Code import status (Stan Hu) - Fix bug where commit data would not appear in some subdirectories (Stan Hu) - - Unescape branch names in compare commit (Stan Hu) - Task lists are now usable in comments, and will show up in Markdown previews. - Fix bug where avatar filenames were not actually deleted from the database during removal (Stan Hu) - Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu) - Protect OmniAuth request phase against CSRF. - - - - + - Don't send notifications to mentioned users that don't have access to the project in question. + - Add search issues/MR by number - Move snippets UI to fluid layout - Improve UI for sidebar. Increase separation between navigation and content - Improve new project command options (Ben Bodenmiller) @@ -68,6 +88,15 @@ v 7.11.0 (unreleased) - Fix reference links in dashboard activity and ATOM feeds. - Ensure that the first added admin performs repository imports +v 7.10.4 + - Fix migrations broken in 7.10.2 + - Make tags for GitLab installations running on MySQL case sensitive + - Get Gitorious importer to work again. + - Fix adding new group members from admin area + - Fix DB error when trying to tag a repository (Stan Hu) + - Fix Error 500 when searching Wiki pages (Stan Hu) + - Unescape branch names in compare commit (Stan Hu) + v 7.10.2 - Fix CI links on MR page diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 895202b58e2..8059b95609a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,9 @@ If you can, please submit a merge request with the fix or improvements including 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). 1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk. -The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as assisting subscribers with upgrade issues, the release of Enterprise Edition and the upgrade of GitLab Cloud. After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features. +The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. +Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as regressions requiring patch releases. +After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features. Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it. @@ -160,8 +162,9 @@ If you add a dependency in GitLab (such as an operating system package) please c 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) 1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) +1. [Database Migrations](doc/development/migration_style_guide.md) +1. [Documentation styleguide](doc_styleguide.md) 1. Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). -1. [Migrations](doc/development/migration_style_guide.md) This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). @@ -177,4 +180,4 @@ Project maintainers have the right and responsibility to remove, edit, or reject Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com -This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) +This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
\ No newline at end of file @@ -44,7 +44,7 @@ gem "browser" # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '~> 7.1.12' +gem "gitlab_git", '~> 7.1.13' # Ruby/Rack Git Smart-HTTP Server Handler gem 'gitlab-grack', '~> 2.0.2', require: 'grack' @@ -94,7 +94,7 @@ gem "seed-fu" # Markdown and HTML processing gem 'html-pipeline', '~> 1.11.0' -gem 'task_list', '~> 1.0.0', require: 'task_list/railtie' +gem 'task_list', '1.0.2', require: 'task_list/railtie' gem 'github-markup' gem 'redcarpet', '~> 3.2.3' gem 'RedCloth' @@ -102,7 +102,7 @@ gem 'rdoc', '~>3.6' gem 'org-ruby', '= 0.9.12' gem 'creole', '~>0.3.6' gem 'wikicloth', '=0.8.1' -gem 'asciidoctor', '= 0.1.4' +gem 'asciidoctor', '~> 1.5.2' # Diffs gem 'diffy', '~> 3.0.3' @@ -172,7 +172,7 @@ gem "underscore-rails", "~> 1.4.4" gem "sanitize", '~> 2.0' # Protect against bruteforcing -gem "rack-attack" +gem "rack-attack", '~> 4.3.0' # Ace editor gem 'ace-rails-ap' @@ -239,7 +239,7 @@ group :development, :test do gem 'minitest', '~> 5.3.0' # Generate Fake data - gem "ffaker" + gem 'ffaker', '~> 2.0.0' # Guard gem 'guard-rspec' diff --git a/Gemfile.lock b/Gemfile.lock index 571ab27ea71..80e4a44c1da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,7 +42,7 @@ GEM arel (5.0.1.20140414130214) asana (0.0.6) activeresource (>= 3.2.3) - asciidoctor (0.1.4) + asciidoctor (1.5.2) ast (2.0.0) astrolabe (1.3.0) parser (>= 2.2.0.pre.3, < 3.0) @@ -176,7 +176,7 @@ GEM faraday_middleware (0.9.0) faraday (>= 0.7.4, < 0.9) fastercsv (1.5.5) - ffaker (1.22.1) + ffaker (2.0.0) ffi (1.9.8) fog (1.21.0) fog-brightbox @@ -225,11 +225,11 @@ GEM mime-types (~> 1.19) gitlab_emoji (0.1.0) gemojione (~> 2.0) - gitlab_git (7.1.12) + gitlab_git (7.1.13) activesupport (~> 4.0) charlock_holmes (~> 0.6) gitlab-linguist (~> 3.0) - rugged (~> 0.21.2) + rugged (~> 0.22.2) gitlab_meta (7.0) gitlab_omniauth-ldap (1.2.1) net-ldap (~> 0.9) @@ -338,7 +338,7 @@ GEM method_source (0.8.2) mime-types (1.25.1) mimemagic (0.3.0) - mini_portile (0.6.1) + mini_portile (0.6.2) minitest (5.3.5) mousetrap-rails (1.4.6) multi_json (1.10.1) @@ -350,7 +350,7 @@ GEM net-ssh (>= 2.6.5) net-ssh (2.8.0) newrelic_rpm (3.9.4.245) - nokogiri (1.6.5) + nokogiri (1.6.6.2) mini_portile (~> 0.6.0) nprogress-rails (0.1.2.3) oauth (0.4.7) @@ -421,7 +421,7 @@ GEM rack (1.5.2) rack-accept (0.4.5) rack (>= 0.4) - rack-attack (4.2.0) + rack-attack (4.3.0) rack rack-cors (0.2.9) rack-mini-profiler (0.9.0) @@ -530,7 +530,7 @@ GEM sexp_processor (~> 4.1) rubyntlm (0.5.0) rubypants (0.2.0) - rugged (0.21.4) + rugged (0.22.2) rugments (1.0.0.beta6) safe_yaml (0.9.7) sanitize (2.1.0) @@ -547,9 +547,9 @@ GEM sdoc (0.3.20) json (>= 1.1.3) rdoc (~> 3.10) - seed-fu (2.3.1) - activerecord (>= 3.1, < 4.2) - activesupport (>= 3.1, < 4.2) + seed-fu (2.3.5) + activerecord (>= 3.1, < 4.3) + activesupport (>= 3.1, < 4.3) select2-rails (3.5.2) thor (~> 0.14) settingslogic (2.0.9) @@ -589,7 +589,7 @@ GEM capybara (>= 2.0.0) railties (>= 3) spinach (>= 0.4) - spring (1.3.3) + spring (1.3.6) spring-commands-rspec (1.0.4) spring (>= 0.9.1) spring-commands-spinach (1.0.0) @@ -683,7 +683,7 @@ DEPENDENCIES addressable annotate (~> 2.6.0.beta2) asana (~> 0.0.6) - asciidoctor (= 0.1.4) + asciidoctor (~> 1.5.2) attr_encrypted (= 1.3.4) awesome_print better_errors @@ -713,7 +713,7 @@ DEPENDENCIES email_spec enumerize factory_girl_rails - ffaker + ffaker (~> 2.0.0) fog (~> 1.14) font-awesome-rails (~> 4.2) foreman @@ -723,7 +723,7 @@ DEPENDENCIES gitlab-grack (~> 2.0.2) gitlab-linguist (~> 3.0.1) gitlab_emoji (~> 0.1) - gitlab_git (~> 7.1.12) + gitlab_git (~> 7.1.13) gitlab_meta (= 7.0) gitlab_omniauth-ldap (= 1.2.1) gollum-lib (~> 4.0.2) @@ -764,7 +764,7 @@ DEPENDENCIES poltergeist (~> 1.5.1) pry-rails quiet_assets (~> 1.0.1) - rack-attack + rack-attack (~> 4.3.0) rack-cors rack-mini-profiler rack-oauth2 (~> 1.0.5) @@ -802,7 +802,7 @@ DEPENDENCIES spring-commands-spinach (= 1.0.0) stamp state_machine - task_list (~> 1.0.0) + task_list (= 1.0.2) test_after_commit thin tinder (~> 1.9.2) diff --git a/README.md b/README.md index 1ceac4621d9..85ea5c876af 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ 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 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/). +To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/). ## Code status @@ -1 +1 @@ -7.11.0.pre +7.12.0.pre
\ No newline at end of file diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index bb9da147018..caf18c0d860 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -140,8 +140,8 @@ $ -> # Place the logo tooltip on the right when collapsed, bottom when expanded $el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom' else - # Otherwise use the data-placement attribute like normal - $el.data('placement') + # Otherwise use the data-placement attribute, or 'bottom' if undefined + $el.data('placement') or 'bottom' }) # Form submitter diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index c25b1ddb066..f186fec2a0c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -312,6 +312,14 @@ class @Notes form.show() textarea = form.find("textarea") textarea.focus() + + # HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?). + # The textarea has the correct value, Chrome just won't show it unless we + # modify it, so let's clear it and re-set it! + value = textarea.val() + textarea.val "" + textarea.val value + disableButtonIfEmptyField textarea, form.find(".js-comment-button") ### diff --git a/app/assets/javascripts/wikis.js.coffee b/app/assets/javascripts/wikis.js.coffee index 66757565d3a..81cfc37b956 100644 --- a/app/assets/javascripts/wikis.js.coffee +++ b/app/assets/javascripts/wikis.js.coffee @@ -1,9 +1,17 @@ class @Wikis constructor: -> - $('.build-new-wiki').bind "click", -> + $('.build-new-wiki').bind "click", (e) -> + $('[data-error~=slug]').addClass("hidden") + $('p.hint').show() field = $('#new_wiki_path') - slug = field.val() - path = field.attr('data-wikis-path') + valid_slug_pattern = /^[\w\/-]+$/ - if(slug.length > 0) - location.href = path + "/" + slug + slug = field.val() + if slug.match valid_slug_pattern + path = field.attr('data-wikis-path') + if(slug.length > 0) + location.href = path + "/" + slug + else + e.preventDefault() + $('p.hint').hide() + $('[data-error~=slug]').removeClass("hidden") diff --git a/app/assets/javascripts/zen_mode.js.coffee b/app/assets/javascripts/zen_mode.js.coffee index 0fb8f7ed75f..dc6a84c6c52 100644 --- a/app/assets/javascripts/zen_mode.js.coffee +++ b/app/assets/javascripts/zen_mode.js.coffee @@ -1,6 +1,4 @@ class @ZenMode - @fullscreen_prefix = 'fullscreen_' - constructor: -> @active_zen_area = null @active_checkbox = null @@ -12,18 +10,18 @@ class @ZenMode $('body').on 'click', '.zen-enter-link', (e) => e.preventDefault() - $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true) + $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true).change() $('body').on 'click', '.zen-leave-link', (e) => e.preventDefault() - $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false) + $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false).change() $('body').on 'change', '.zen-toggle-comment', (e) => checkbox = e.currentTarget if checkbox.checked # Disable other keyboard shortcuts in ZEN mode Mousetrap.pause() - @udpateActiveZenArea(checkbox) + @updateActiveZenArea(checkbox) else @exitZenMode() @@ -32,14 +30,11 @@ class @ZenMode @exitZenMode() e.preventDefault() - $(window).on 'hashchange', @updateZenModeFromLocationHash - - udpateActiveZenArea: (checkbox) => + updateActiveZenArea: (checkbox) => @active_checkbox = $(checkbox) @active_checkbox.prop('checked', true) @active_zen_area = @active_checkbox.parent().find('textarea') @active_zen_area.focus() - window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id') exitZenMode: => if @active_zen_area isnt null @@ -51,17 +46,3 @@ class @ZenMode window.scrollTo(window.pageXOffset, @scroll_position) # Enable dropzone when leaving ZEN mode Dropzone.forElement('.div-dropzone').enable() - - checkboxFromLocationHash: (e) -> - id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, '')) - if id - return $('.zennable input[type=checkbox]#' + id)[0] - else - return null - - updateZenModeFromLocationHash: (e) => - checkbox = @checkboxFromLocationHash() - if checkbox - @udpateActiveZenArea(checkbox) - else - @exitZenMode() diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss index 266041403e0..7e070b4f386 100644 --- a/app/assets/stylesheets/generic/forms.scss +++ b/app/assets/stylesheets/generic/forms.scss @@ -89,7 +89,6 @@ label { @include box-shadow(none); } -.issuable-description, .wiki-content { margin-top: 35px; } diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/generic/header.scss index fcd62373bfd..c4bafad6906 100644 --- a/app/assets/stylesheets/generic/header.scss +++ b/app/assets/stylesheets/generic/header.scss @@ -184,28 +184,17 @@ header { padding: 4px 6px; padding-left: 25px; font-size: 13px; - @include border-radius(3px); - border: 1px solid #DDD; - box-shadow: none; - @include transition(all 0.15s ease-in 0s); - background-color: #f5f5f5; } } } .search .search-input { width: 300px; - &:focus { - width: 330px; - } } @media (max-width: 1200px) { .search .search-input { width: 200px; - &:focus { - width: 230px; - } } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index e943be67dbf..42b8ecabb38 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -79,11 +79,11 @@ ul.notes { word-wrap: break-word; @include md-typography; - // Reduce left padding of first ul element + // Reduce left padding of first task list ul element ul.task-list:first-child { padding-left: 10px; - // sub-lists should be padded normally + // sub-tasks should be padded normally ul { padding-left: 20px; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 49e5aad1f67..83771480cbd 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -129,7 +129,7 @@ } .option-descr { - margin-left: 24px; + margin-left: 36px; color: $gray; } } @@ -230,6 +230,10 @@ ul.nav.nav-projects-tabs { margin: 10px 0; } } + + .ci-status-image { + max-height: 22px; + } } .transfer-project .select2-container { diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 88459d4080a..145f27b67dd 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -36,4 +36,24 @@ class PasswordsController < Devise::PasswordsController end end end + + def edit + super + reset_password_token = Devise.token_generator.digest( + User, + :reset_password_token, + resource.reset_password_token + ) + + unless reset_password_token.nil? + user = User.where( + reset_password_token: reset_password_token + ).first_or_initialize + + unless user.reset_password_period_valid? + flash[:alert] = 'Your password reset token has expired.' + redirect_to(new_user_password_url(user_email: user['email'])) + end + end + end end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 57fc48ac7da..76062446c92 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -53,6 +53,6 @@ class Projects::HooksController < Projects::ApplicationController end def hook_params - params.require(:hook).permit(:url, :push_events, :issues_events, :merge_requests_events, :tag_push_events) + params.require(:hook).permit(:url, :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events) end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index c524e1a0ea3..7d168aa827b 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -19,7 +19,15 @@ class Projects::IssuesController < Projects::ApplicationController def index terms = params['issue_search'] @issues = get_issues_collection - @issues = @issues.full_search(terms) if terms.present? + + if terms.present? + if terms =~ /\A#(\d+)\z/ + @issues = @issues.where(iid: $1) + else + @issues = @issues.full_search(terms) + end + end + @issues = @issues.page(params[:page]).per(PER_PAGE) respond_to do |format| diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 5b93e95866a..c7467e9b2f5 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -19,7 +19,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController def index terms = params['issue_search'] @merge_requests = get_merge_requests_collection - @merge_requests = @merge_requests.full_search(terms) if terms.present? + + if terms.present? + if terms =~ /\A[#!](\d+)\z/ + @merge_requests = @merge_requests.where(iid: $1) + else + @merge_requests = @merge_requests.full_search(terms) + end + end + @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) respond_to do |format| diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ea9722b9bef..bcd400b7e7b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -222,18 +222,28 @@ module ApplicationHelper end def render_markup(file_name, file_content) - GitHub::Markup.render(file_name, file_content). - force_encoding(file_content.encoding).html_safe + if gitlab_markdown?(file_name) + Haml::Helpers.preserve(markdown(file_content)) + elsif asciidoc?(file_name) + asciidoc(file_content) + else + GitHub::Markup.render(file_name, file_content). + force_encoding(file_content.encoding).html_safe + end rescue RuntimeError simple_format(file_content) end def markup?(filename) - Gitlab::MarkdownHelper.markup?(filename) + Gitlab::MarkupHelper.markup?(filename) end def gitlab_markdown?(filename) - Gitlab::MarkdownHelper.gitlab_markdown?(filename) + Gitlab::MarkupHelper.gitlab_markdown?(filename) + end + + def asciidoc?(filename) + Gitlab::MarkupHelper.asciidoc?(filename) end # Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 4ea838ca447..9fe5f82f02f 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -9,11 +9,13 @@ module BlobHelper begin lexer = Rugments::Lexer.guess(filename: blob_name, source: blob_content) - rescue Rugments::Lexer::AmbiguousGuess + result = formatter.format(lexer.lex(blob_content)).html_safe + rescue lexer = Rugments::Lexers::PlainText + result = formatter.format(lexer.lex(blob_content)).html_safe end - formatter.format(lexer.lex(blob_content)).html_safe + result end def no_highlight_files @@ -55,7 +57,7 @@ module BlobHelper end def editing_preview_title(filename) - if Gitlab::MarkdownHelper.previewable?(filename) + if Gitlab::MarkupHelper.previewable?(filename) 'Preview' else 'Preview changes' diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 1b10795bb7b..1bd3ec5e0e0 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -101,6 +101,10 @@ module DiffHelper (bottom) ? 'js-unfold-bottom' : '' end + def unfold_class(unfold) + (unfold) ? 'unfold js-unfold' : '' + end + def diff_line_content(line) if line.blank? " " diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index 0df3ecc90b7..128de18bc47 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -35,4 +35,23 @@ module EmailsHelper lexer = Rugments::Lexers::Diff.new raw formatter.format(lexer.lex(diffcontent)) end + + def password_reset_token_valid_time + valid_hours = Devise.reset_password_within / 60 / 60 + if valid_hours >= 24 + unit = 'day' + valid_length = (valid_hours / 24).floor + else + unit = 'hour' + valid_length = valid_hours.floor + end + + pluralize(valid_length, unit) + end + + def reset_token_expire_message + link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email)) + msg = "This link is valid for #{password_reset_token_valid_time}. " + msg << "After it expires, you can #{link_tag}." + end end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 846aded4bda..7bcc011fd5f 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -56,6 +56,16 @@ module GitlabMarkdownHelper @markdown.render(text).html_safe end + def asciidoc(text) + Gitlab::Asciidoc.render(text, { + commit: @commit, + project: @project, + project_wiki: @project_wiki, + requested_path: @path, + ref: @ref + }) + end + # Return the first line of +text+, up to +max_chars+, after parsing the line # as Markdown. HTML tags in the parsed output are not counted toward the # +max_chars+ limit. If the length limit falls within a tag's contents, then @@ -67,8 +77,11 @@ module GitlabMarkdownHelper end def render_wiki_content(wiki_page) - if wiki_page.format == :markdown + case wiki_page.format + when :markdown markdown(wiki_page.content) + when :asciidoc + asciidoc(wiki_page.content) else wiki_page.formatted_content.html_safe end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 8272c177d59..8036303851b 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -1,6 +1,44 @@ module LabelsHelper include ActionView::Helpers::TagHelper + # Link to a Label + # + # label - Label object to link to + # project - Project object which will be used as the context for the label's + # link. If omitted, defaults to `@project`, or the label's own + # project. + # block - An optional block that will be passed to `link_to`, forming the + # body of the link element. If omitted, defaults to + # `render_colored_label`. + # + # Examples: + # + # # Allow the generated link to use the label's own project + # link_to_label(label) + # + # # Force the generated link to use @project + # @project = Project.first + # link_to_label(label) + # + # # Force the generated link to use a provided project + # link_to_label(label, project: Project.last) + # + # # Customize link body with a block + # link_to_label(label) { "My Custom Label Text" } + # + # Returns a String + def link_to_label(label, project: nil, &block) + project ||= @project || label.project + link = namespace_project_issues_path(project.namespace, project, + label_name: label.name) + + if block_given? + link_to link, &block + else + link_to render_colored_label(label), link + end + end + def project_label_names @project.labels.pluck(:title) end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 96d2606f1a1..f8df39d236a 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -148,7 +148,7 @@ module ProjectsHelper nav_tabs << [:files, :commits, :network, :graphs] end - if project.repo_exists? && project.merge_requests_enabled + if project.repo_exists? && can?(current_user, :read_merge_request, project) nav_tabs << :merge_requests end @@ -156,11 +156,19 @@ module ProjectsHelper nav_tabs << :settings end - [:issues, :wiki, :snippets].each do |feature| - nav_tabs << feature if project.send :"#{feature}_enabled" + if can?(current_user, :read_issue, project) + nav_tabs << :issues end - if project.issues_enabled || project.merge_requests_enabled + if can?(current_user, :read_wiki, project) + nav_tabs << :wiki + end + + if can?(current_user, :read_project_snippet, project) + nav_tabs << :snippets + end + + if can?(current_user, :read_milestone, project) nav_tabs << [:milestones, :labels] end diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index bec8f2f1aa7..2b99a398049 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -10,6 +10,7 @@ module SelectsHelper any_user = opts[:any_user] || false email_user = opts[:email_user] || false first_user = opts[:first_user] && current_user ? current_user.username : false + project = opts[:project] || @project html = { class: css_class, @@ -21,8 +22,8 @@ module SelectsHelper } unless opts[:scope] == :all - if @project - html['data-project-id'] = @project.id + if project + html['data-project-id'] = project.id elsif @group html['data-group-id'] = @group.id end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 6dd9b6f017c..03a49e119b8 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -25,13 +25,7 @@ module TreeHelper end def render_readme(readme) - if gitlab_markdown?(readme.name) - preserve(markdown(readme.data)) - elsif markup?(readme.name) - render_markup(readme.name, readme.data) - else - simple_format(readme.data) - end + render_markup(readme.name, readme.data) end # Return an image icon depending on the file type and mode diff --git a/app/models/ability.rb b/app/models/ability.rb index 85a15596f8d..e166b4197fd 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -101,6 +101,27 @@ class Ability rules -= project_archived_rules end + unless project.issues_enabled + rules -= named_abilities('issue') + end + + unless project.merge_requests_enabled + rules -= named_abilities('merge_request') + end + + unless project.issues_enabled or project.merge_requests_enabled + rules -= named_abilities('label') + rules -= named_abilities('milestone') + end + + unless project.snippets_enabled + rules -= named_abilities('snippet') + end + + unless project.wiki_enabled + rules -= named_abilities('wiki') + end + rules end end @@ -272,5 +293,16 @@ class Ability abilities end end + + private + + def named_abilities(name) + [ + :"read_#{name}", + :"write_#{name}", + :"modify_#{name}", + :"admin_#{name}" + ] + end end end diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index a4832204f7b..9f667f47e0d 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -35,8 +35,8 @@ module Participable end end - def participants(current_user = self.author) - self.class.participant_attrs.flat_map do |attr| + def participants(current_user = self.author, project = self.project) + participants = self.class.participant_attrs.flat_map do |attr| meth = method(attr) value = @@ -46,20 +46,28 @@ module Participable meth.call end - participants_for(value, current_user) + participants_for(value, current_user, project) end.compact.uniq + + if project + participants.select! do |user| + user.can?(:read_project, project) + end + end + + participants end private - def participants_for(value, current_user = nil) + def participants_for(value, current_user = nil, project = nil) case value when User [value] when Enumerable, ActiveRecord::Relation - value.flat_map { |v| participants_for(v, current_user) } + value.flat_map { |v| participants_for(v, current_user, project) } when Participable - value.participants(current_user) + value.participants(current_user, project) end end end diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index 7e4f16ebf16..ab055f6b80b 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -44,7 +44,7 @@ class GroupMilestone def percent_complete ((closed_items_count * 100) / total_items_count).abs rescue ZeroDivisionError - 100 + 0 end def state diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index 21867a9316c..ca7066b959a 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # class ProjectHook < WebHook @@ -21,5 +22,6 @@ class ProjectHook < WebHook scope :push_hooks, -> { where(push_events: true) } scope :tag_push_hooks, -> { where(tag_push_events: true) } scope :issue_hooks, -> { where(issues_events: true) } + scope :note_hooks, -> { where(note_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) } end diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index 5b38ade2e6b..b55e217975f 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # class ServiceHook < WebHook diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index ee32b49bc66..6fb2d421026 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # class SystemHook < WebHook diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index e9fd441352d..46fb85336e5 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # class WebHook < ActiveRecord::Base @@ -21,6 +22,7 @@ class WebHook < ActiveRecord::Base default_value_for :push_events, true default_value_for :issues_events, false + default_value_for :note_events, false default_value_for :merge_requests_events, false default_value_for :tag_push_events, false diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 9bbb2bafb98..9c543b37023 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -66,7 +66,7 @@ class Milestone < ActiveRecord::Base def percent_complete ((closed_items_count * 100) / total_items_count).abs rescue ZeroDivisionError - 100 + 0 end def expires_at diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 0706a1ca0d1..231973fa543 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -2,7 +2,7 @@ class ProjectWiki include Gitlab::ShellAdapter MARKUPS = { - 'Markdown' => :markdown, + 'Markdown' => :md, 'RDoc' => :rdoc, 'AsciiDoc' => :asciidoc } unless defined?(MARKUPS) diff --git a/app/models/tree.rb b/app/models/tree.rb index f279e896cda..93b3246a668 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -1,11 +1,11 @@ class Tree - include Gitlab::MarkdownHelper + include Gitlab::MarkupHelper attr_accessor :repository, :sha, :path, :entries def initialize(repository, sha, path = '/') path = '/' if path.blank? - + @repository = repository @sha = sha @path = path @@ -20,7 +20,7 @@ class Tree available_readmes = blobs.select(&:readme?) if available_readmes.count == 0 - return @readme = nil + return @readme = nil end # Take the first previewable readme, or the first available readme, if we diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index d19a6c2eca3..0ff37c41743 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -31,7 +31,7 @@ module Notes def execute_hooks(note) note_data = hook_data(note) - # TODO: Support Webhooks + note.project.execute_hooks(note_data, :note_hooks) note.project.execute_services(note_data, :note_hooks) end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 0d7ffbeebd9..312b56eb87b 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -91,10 +91,14 @@ class NotificationService # * project team members with notification level higher then Participating # def merge_mr(merge_request, current_user) - recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project) + recipients = [merge_request.author, merge_request.assignee] + + recipients = add_project_watchers(recipients, merge_request.target_project) + recipients = reject_muted_users(recipients, merge_request.target_project) + recipients = add_subscribed_users(recipients, merge_request) recipients = reject_unsubscribed_users(recipients, merge_request) - recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq + recipients.delete(current_user) recipients.each do |recipient| @@ -137,20 +141,17 @@ class NotificationService recipients = recipients.concat(participants) # Merge project watchers - recipients = recipients.concat(project_watchers(note.project)).compact.uniq + recipients = add_project_watchers(recipients, note.project) # Reject users with Mention notification level, except those mentioned in _this_ note. recipients = reject_mention_users(recipients - note.mentioned_users, note.project) recipients = recipients + note.mentioned_users - # Reject mutes users recipients = reject_muted_users(recipients, note.project) recipients = add_subscribed_users(recipients, note.noteable) - recipients = reject_unsubscribed_users(recipients, note.noteable) - # Reject author recipients.delete(note.author) # build notify method like 'note_commit_email' @@ -287,6 +288,10 @@ class NotificationService users end + def add_project_watchers(recipients, project) + recipients.concat(project_watchers(project)).compact.uniq + end + # Remove users with disabled notifications from array # Also remove duplications and nil recipients def reject_muted_users(users, project = nil) @@ -403,11 +408,13 @@ class NotificationService [target.author, target.assignee] end - recipients = reject_muted_users(recipients, project) + recipients = add_project_watchers(recipients, project) recipients = reject_mention_users(recipients, project) + recipients = reject_muted_users(recipients, project) + recipients = add_subscribed_users(recipients, target) - recipients = recipients.concat(project_watchers(project)).uniq recipients = reject_unsubscribed_users(recipients, target) + recipients end diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 6e88fc9be40..07bda1c77f8 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{current_user.name} issues" - xml.link href: issues_dashboard_url(format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml" + xml.link href: issues_dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: issues_dashboard_url, rel: "alternate", type: "text/html" xml.id issues_dashboard_url xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any? diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder index 71edb73cd8a..e9a612231d5 100644 --- a/app/views/dashboard/show.atom.builder +++ b/app/views/dashboard/show.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "Activity" - xml.link href: dashboard_url(format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml" + xml.link href: dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: dashboard_url, rel: "alternate", type: "text/html" xml.id dashboard_url xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml index e8820daf58f..29ffe8a8be3 100644 --- a/app/views/devise/passwords/new.html.haml +++ b/app/views/devise/passwords/new.html.haml @@ -6,7 +6,7 @@ .devise-errors = devise_error_messages! .clearfix.append-bottom-20 - = f.email_field :email, placeholder: "Email", class: "form-control", required: true + = f.email_field :email, placeholder: "Email", class: "form-control", required: true, value: params[:user_email] .clearfix = f.submit "Reset password", class: "btn-primary btn" diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml index 42762e04b51..3625cb49d8b 100644 --- a/app/views/events/_event_push.atom.haml +++ b/app/views/events/_event_push.atom.haml @@ -6,7 +6,7 @@ %i at = commit[:timestamp].to_time.to_s(:short) - %blockquote= markdown(escape_once(commit[:message]), xhtml: true, reference_only_path: false, project: note.project) + %blockquote= markdown(escape_once(commit[:message]), xhtml: true, reference_only_path: false, project: event.project) - if event.commits_count > 15 %p %i diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index b52e78faaa3..a91d1a6e94b 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@group.name} activity" - xml.link href: group_url(@group, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml" + xml.link href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: group_url(@group), rel: "alternate", type: "text/html" xml.id group_url(@group) xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 04f79846858..e2d2dec7ab8 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -1,6 +1,6 @@ .search = form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f| - = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input" + = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control" = hidden_field_tag :group_id, @group.try(:id) - if @project && @project.persisted? = hidden_field_tag :project_id, @project.id diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml index ebbe98dd472..4feacdaacff 100644 --- a/app/views/notify/new_user_email.html.haml +++ b/app/views/notify/new_user_email.html.haml @@ -11,4 +11,6 @@ - if @user.created_by_id %p - = link_to "Click here to set your password", edit_password_url(@user, :reset_password_token => @token) + = link_to "Click here to set your password", edit_password_url(@user, reset_password_token: @token) + %p + = reset_token_expire_message diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb index 96b26879a77..dd9b71e3b84 100644 --- a/app/views/notify/new_user_email.text.erb +++ b/app/views/notify/new_user_email.text.erb @@ -5,4 +5,6 @@ The Administrator created an account for you. Now you are a member of the compan login.................. <%= @user.email %> <% if @user.created_by_id %> <%= link_to "Click here to set your password", edit_password_url(@user, :reset_password_token => @token) %> + + <%= reset_token_expire_message %> <% end %> diff --git a/app/views/projects/_aside.html.haml b/app/views/projects/_aside.html.haml index 333a1e6156d..e90c7b26dd2 100644 --- a/app/views/projects/_aside.html.haml +++ b/app/views/projects/_aside.html.haml @@ -57,7 +57,7 @@ .pull-right - if ci_service.respond_to?(:status_img_path) = link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do - = image_tag ci_service.status_img_path, alt: "build status" + = image_tag ci_service.status_img_path, alt: "build status", class: 'ci-status-image' - else = link_to 'view builds', ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' diff --git a/app/views/projects/_dropdown.html.haml b/app/views/projects/_dropdown.html.haml deleted file mode 100644 index d623a3716ed..00000000000 --- a/app/views/projects/_dropdown.html.haml +++ /dev/null @@ -1,37 +0,0 @@ -- if current_user - .dropdown.pull-right - %a.dropdown-toggle.btn.btn-sm{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-bars - %ul.dropdown-menu - - if @project.issues_enabled && can?(current_user, :write_issue, @project) - %li - = link_to url_for_new_issue(@project, only_path: true), title: "New Issue" do - %i.fa.fa-fw.fa-exclamation-circle - New issue - - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project) - %li - = link_to new_namespace_project_merge_request_path(@project.namespace, @project), title: "New Merge Request" do - %i.fa.fa-fw.fa-tasks - New merge request - - if @project.snippets_enabled && can?(current_user, :write_snippet, @project) - %li - = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do - %i.fa.fa-fw.fa-file-text-o - New snippet - - if can?(current_user, :admin_project_member, @project) - %li - = link_to namespace_project_project_members_path(@project.namespace, @project), title: "New project member" do - %i.fa.fa-fw.fa-users - New project member - - if can? current_user, :push_code, @project - %li.divider - %li - = link_to new_namespace_project_branch_path(@project.namespace, @project) do - %i.fa.fa-fw.fa-code-fork - New branch - %li - = link_to new_namespace_project_tag_path(@project.namespace, @project) do - %i.fa.fa-fw.fa-tag - New tag - - diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml index e321a84974e..2292aaaa214 100644 --- a/app/views/projects/_issuable_form.html.haml +++ b/app/views/projects/_issuable_form.html.haml @@ -11,6 +11,15 @@ .col-sm-10 = f.text_field :title, maxlength: 255, autofocus: true, class: 'form-control pad js-gfm-input', required: true + + - if issuable.is_a?(MergeRequest) + %p.help-block + - if issuable.work_in_progress? + This merge request is marked a <strong>Work In Progress</strong>. + When it's ready, remove the <code>WIP</code> prefix from the title to allow it to be accepted. + - else + To prevent this merge request from being accepted before it's ready, + mark it a <strong>Work In Progress</strong> by starting the title with <code>[WIP]</code> or <code>WIP:</code>. .form-group.issuable-description = f.label :description, 'Description', class: 'control-label' .col-sm-10 @@ -37,7 +46,7 @@ .col-sm-10 = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", placeholder: 'Select a user', class: 'custom-form-control', null_user: true, - selected: issuable.assignee_id) + selected: issuable.assignee_id, project: @target_project || @project) = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' .form-group diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml index f6bd62f239b..4429c395aee 100644 --- a/app/views/projects/blob/_text.html.haml +++ b/app/views/projects/blob/_text.html.haml @@ -1,8 +1,4 @@ -- if gitlab_markdown?(blob.name) - .file-content.wiki - = preserve do - = markdown(blob.data) -- elsif markup?(blob.name) +- if markup?(blob.name) .file-content.wiki = render_markup(blob.name, blob.data) - else diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index 5c79d0ef11f..84742608986 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -2,7 +2,7 @@ - if @form.unfold? && @form.since != 1 && !@form.bottom? %tr.line_holder{ id: @form.since } = render "projects/diffs/match_line", {line: @match_line, - line_old: @form.since, line_new: @form.since, bottom: false} + line_old: @form.since, line_new: @form.since, bottom: false, new_file: false} - @lines.each_with_index do |line, index| - line_new = index + @form.since @@ -16,4 +16,4 @@ - if @form.unfold? && @form.bottom? && @form.to < @blob.loc %tr.line_holder{ id: @form.to } = render "projects/diffs/match_line", {line: @match_line, - line_old: @form.to, line_new: @form.to, bottom: true} + line_old: @form.to, line_new: @form.to, bottom: true, new_file: false} diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 01edd9447ce..3854ad5d611 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@project.name}:#{@ref} commits" - xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml" + xml.link href: namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.try(:private_token)), 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? diff --git a/app/views/projects/diffs/_match_line.html.haml b/app/views/projects/diffs/_match_line.html.haml index 4ebe3379733..d1f897b99f7 100644 --- a/app/views/projects/diffs/_match_line.html.haml +++ b/app/views/projects/diffs/_match_line.html.haml @@ -1,7 +1,7 @@ -%td.old_line.diff-line-num.unfold.js-unfold{data: {linenumber: line_old}, - class: unfold_bottom_class(bottom)} +%td.old_line.diff-line-num{data: {linenumber: line_old}, + class: [unfold_bottom_class(bottom), unfold_class(!new_file)]} \... -%td.new_line.diff-line-num.unfold.js-unfold{data: {linenumber: line_new}, - class: unfold_bottom_class(bottom)} +%td.new_line.diff-line-num{data: {linenumber: line_new}, + class: [unfold_bottom_class(bottom), unfold_class(!new_file)]} \... %td.line_content.matched= line diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index e6dfbfd6511..a6373181b45 100644 --- a/app/views/projects/diffs/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -12,7 +12,7 @@ %tr.line_holder{ id: line_code, class: "#{type}" } - if type == "match" = render "projects/diffs/match_line", {line: line.text, - line_old: line_old, line_new: line.new_pos, bottom: false} + line_old: line_old, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file} - else %td.old_line = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code @@ -29,7 +29,7 @@ - if last_line > 0 = render "projects/diffs/match_line", {line: "", - line_old: last_line, line_new: last_line, bottom: true} + line_old: last_line, line_new: last_line, bottom: true, new_file: diff_file.new_file} - if diff_file.diff.blank? && diff_file.mode_changed? .file-mode-changed diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 808c03148f4..eadbf61fdd4 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -35,6 +35,13 @@ %p.light This url will be triggered when a new tag is pushed to the repository %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 + %div = f.check_box :issues_events, class: 'pull-left' .prepend-left-20 = f.label :issues_events, class: 'list-label' do @@ -64,6 +71,6 @@ .clearfix %span.monospace= hook.url %p - - %w(push_events tag_push_events issues_events merge_requests_events).each do |trigger| + - %w(push_events tag_push_events issues_events note_events merge_requests_events).each do |trigger| - if hook.send(trigger) %span.label.label-gray= trigger.titleize diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 2016f5c709c..48858fa32da 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -30,5 +30,4 @@ %label 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_label(label) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index ef36d1f9547..a4e25e5ce88 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -8,8 +8,7 @@ = link_to_gfm issue.title, issue_path(issue), class: "row_title" .issue-labels - issue.labels.each do |label| - = link_to namespace_project_issues_path(issue.project.namespace, issue.project, label_name: label.name) do - = render_colored_label(label) + = link_to_label(label, project: issue.project) .pull-right.light - if issue.closed? %span diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index 5fa8fbdf893..dc8e477185b 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -1,7 +1,7 @@ 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: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml" + xml.link href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), 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? diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index 82829452862..7fa1ee53f76 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -1,8 +1,8 @@ %li{id: dom_id(label)} - = render_colored_label(label) + = link_to_label(label) .pull-right %strong.append-right-20 - = link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do + = link_to_label(label) do = pluralize label.open_issues_count, 'open issue' - if can? current_user, :admin_label, @project diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 9a2aa9c3de0..eb3dba6858d 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -27,5 +27,4 @@ %label 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_label(label) diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 5d5a23b5409..073476b0d27 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -4,8 +4,7 @@ = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" .merge-request-labels - merge_request.labels.each do |label| - = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, label_name: label.name) do - = render_colored_label(label) + = link_to_label(label, project: merge_request.project) .pull-right.light - if merge_request.merged? %span diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml index a5a821c1847..1d0e2e350b0 100644 --- a/app/views/projects/merge_requests/show/_context.html.haml +++ b/app/views/projects/merge_requests/show/_context.html.haml @@ -9,7 +9,7 @@ none .issuable-context-selectbox - if can?(current_user, :modify_merge_request, @merge_request) - = users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id, null_user: true) + = users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id, project: @target_project, null_user: true) %div.prepend-top-20.clearfix .issuable-context-title 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 cb536214c69..882b219f6e2 100644 --- a/app/views/projects/merge_requests/show/_mr_accept.html.haml +++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml @@ -6,7 +6,7 @@ .automerge_widget.cannot_be_merged.hide %strong This request can't be merged automatically. Even if it could be merged, you don't have permission to do so. .automerge_widget.work_in_progress.hide - %strong This request can't be merged automatically because it is marked a Work In Progress. Even if it could be merged, you don't have permission to do so. + %strong This request can't be accepted because it is marked a Work In Progress. Even if it could be accepted, you don't have permission to do so. .automerge_widget.can_be_merged.hide %strong This request can be merged automatically, but you don't have permission to do so. @@ -57,11 +57,11 @@ %i.fa.fa-warning Accept Merge Request - This usually happens when git can not resolve conflicts between branches automatically. + This usually happens when Git can not resolve conflicts between branches automatically. .automerge_widget.work_in_progress.hide %h4 - This request can't be merged because it is marked a <strong>Work In Progress</strong>. + This request can't be accepted because it is marked a <strong>Work In Progress</strong>. %p %button.btn.disabled{:type => 'button'} @@ -69,7 +69,7 @@ Accept Merge Request - When the merge request is ready, remove the "WIP" prefix from the title to allow it to be merged. + When the merge request is ready, remove the "WIP" prefix from the title to allow it to be accepted. .automerge_widget.unchecked %p diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index bba2b8764ac..5845fd744f4 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -61,11 +61,12 @@ Participants %span.badge= @users.count - - if @project.issues_enabled - .pull-right + .pull-right + - if can?(current_user, :write_issue, @project) = 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 + - if can?(current_user, :read_issue, @project) = 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 diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index bb713dcafa5..242684e5c7c 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -1,7 +1,7 @@ xml.instruct! xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.title "#{@project.name} activity" - xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml" + xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html" xml.id namespace_project_url(@project.namespace, @project) xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index 6834969de8b..b2c085f34b1 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -8,6 +8,8 @@ = 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' => namespace_project_wikis_path(@project.namespace, @project) + %p.hidden.text-danger{data: { error: "slug" }} + The page slug is invalid. Please don't use characters other then: a-z 0-9 _ - and / %p.hint Please don't use spaces. .modal-footer diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index 8af393777f0..95099853918 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -13,16 +13,7 @@ .file-title %i.fa.fa-file %strong= snippet_blob[:snippet_object].file_name - - if gitlab_markdown?(snippet_blob[:snippet_object].file_name) - .file-content.wiki - - snippet_blob[:snippet_chunks].each do |snippet| - - unless snippet[:data].empty? - = preserve do - = markdown(snippet[:data]) - - else - .file-content.code - .nothing-here-block Empty file - - elsif markup?(snippet_blob[:snippet_object].file_name) + - if markup?(snippet_blob[:snippet_object].file_name) .file-content.wiki - snippet_blob[:snippet_chunks].each do |snippet| - unless snippet[:data].empty? diff --git a/app/views/shared/_visibility_radios.html.haml b/app/views/shared/_visibility_radios.html.haml index b07c4d20f12..02416125a72 100644 --- a/app/views/shared/_visibility_radios.html.haml +++ b/app/views/shared/_visibility_radios.html.haml @@ -1,7 +1,7 @@ - Gitlab::VisibilityLevel.values.each do |level| .radio - restricted = restricted_visibility_levels.include?(level) - = label model_method, level do + = form.label "#{model_method}_#{level}" do = form.radio_button model_method, level, checked: (selected_level == level), disabled: restricted = visibility_level_icon(level) .option-title diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml index 30458793fd1..d26a99bb14c 100644 --- a/app/views/shared/snippets/_blob.html.haml +++ b/app/views/shared/snippets/_blob.html.haml @@ -1,9 +1,5 @@ - unless @snippet.content.empty? - - if gitlab_markdown?(@snippet.file_name) - .file-content.wiki - = preserve do - = markdown(@snippet.data) - - elsif markup?(@snippet.file_name) + - if markup?(@snippet.file_name) .file-content.wiki = render_markup(@snippet.file_name, @snippet.data) - else diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 6783587bda9..9610f9ce414 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -11,7 +11,7 @@ .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet - + .form-group .file-editor = f.label :file_name, "File", class: 'control-label' @@ -3,6 +3,5 @@ begin load File.expand_path("../spring", __FILE__) rescue LoadError end -require_relative '../config/boot' -require 'rake' -Rake.application.run +require 'bundler/setup' +load Gem.bin_path('rake', 'rake') diff --git a/bin/spring b/bin/spring index 253ec37c345..7b45d374fcd 100755 --- a/bin/spring +++ b/bin/spring @@ -1,17 +1,14 @@ #!/usr/bin/env ruby -# This file loads spring without using Bundler, in order to be fast -# It gets overwritten when you run the `spring binstub` command +# This file loads spring without using Bundler, in order to be fast. +# It gets overwritten when you run the `spring binstub` command. unless defined?(Spring) require "rubygems" require "bundler" - if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ spring \((.*?)\)$.*?^$/m) - ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR) - ENV["GEM_HOME"] = "" - Gem.paths = ENV - + if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m) + Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq } gem "spring", match[1] require "spring/binstub" end diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index bd2081688d1..fbc7f515f34 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -245,6 +245,10 @@ production: &base repos_path: /home/git/repositories/ hooks_path: /home/git/gitlab-shell/hooks/ + # File that contains the secret key for verifying access for gitlab-shell. + # Default is '.gitlab_shell_secret' relative to Rails.root (i.e. root of the GitLab app). + # secret_file: /home/git/gitlab/.gitlab_shell_secret + # Git over HTTP upload_pack: true receive_pack: true diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index e5ac66a2323..2351ef7b0ce 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -148,6 +148,7 @@ Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}? Settings['gitlab_shell'] ||= Settingslogic.new({}) Settings.gitlab_shell['path'] ||= Settings.gitlab['user_home'] + '/gitlab-shell/' Settings.gitlab_shell['hooks_path'] ||= Settings.gitlab['user_home'] + '/gitlab-shell/hooks/' +Settings.gitlab_shell['secret_file'] ||= Rails.root.join('.gitlab_shell_secret') Settings.gitlab_shell['receive_pack'] = true if Settings.gitlab_shell['receive_pack'].nil? Settings.gitlab_shell['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil? Settings.gitlab_shell['repos_path'] ||= Settings.gitlab['user_home'] + '/repositories/' diff --git a/config/initializers/gitlab_shell_secret_token.rb b/config/initializers/gitlab_shell_secret_token.rb index e7c9f0ba7c2..751fccead07 100644 --- a/config/initializers/gitlab_shell_secret_token.rb +++ b/config/initializers/gitlab_shell_secret_token.rb @@ -5,8 +5,7 @@ require 'securerandom' # Your secret key for verifying the gitlab_shell. -secret_file = Rails.root.join('.gitlab_shell_secret') -gitlab_shell_symlink = File.join(Gitlab.config.gitlab_shell.path, '.gitlab_shell_secret') +secret_file = Gitlab.config.gitlab_shell.secret_file unless File.exist? secret_file # Generate a new token of 16 random hexadecimal characters and store it in secret_file. @@ -14,6 +13,7 @@ unless File.exist? secret_file File.write(secret_file, token) end -if File.exist?(Gitlab.config.gitlab_shell.path) && !File.exist?(gitlab_shell_symlink) - FileUtils.symlink(secret_file, gitlab_shell_symlink) +link_path = File.join(Gitlab.config.gitlab_shell.path, '.gitlab_shell_secret') +if File.exist?(Gitlab.config.gitlab_shell.path) && !File.exist?(link_path) + FileUtils.symlink(secret_file, link_path) end diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index ae4c0550a4f..87839770924 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -23,7 +23,7 @@ Sidekiq::Testing.inline! do name: group_path.titleize, path: group_path ) - group.description = Faker::Lorem.sentence + group.description = FFaker::Lorem.sentence group.save group.add_owner(User.first) @@ -35,7 +35,7 @@ Sidekiq::Testing.inline! do import_url: url, namespace_id: group.id, name: project_path.titleize, - description: Faker::Lorem.sentence, + description: FFaker::Lorem.sentence, visibility_level: Gitlab::VisibilityLevel.values.sample } diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb index 24952a1f661..378354efd5a 100644 --- a/db/fixtures/development/05_users.rb +++ b/db/fixtures/development/05_users.rb @@ -2,9 +2,9 @@ Gitlab::Seeder.quiet do (2..20).each do |i| begin User.create!( - username: Faker::Internet.user_name, - name: Faker::Name.name, - email: Faker::Internet.email, + username: FFaker::Internet.user_name, + name: FFaker::Name.name, + email: FFaker::Internet.email, confirmed_at: DateTime.now, password: '12345678' ) diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb index 2296821e528..a43116829d9 100644 --- a/db/fixtures/development/07_milestones.rb +++ b/db/fixtures/development/07_milestones.rb @@ -3,7 +3,7 @@ Gitlab::Seeder.quiet do (1..5).each do |i| milestone_params = { title: "v#{i}.0", - description: Faker::Lorem.sentence, + description: FFaker::Lorem.sentence, state: ['opened', 'closed'].sample, } diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb index e8b01b46d22..c636e96381c 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/09_issues.rb @@ -2,8 +2,8 @@ Gitlab::Seeder.quiet do Project.all.each do |project| (1..10).each do |i| issue_params = { - title: Faker::Lorem.sentence(6), - description: Faker::Lorem.sentence, + title: FFaker::Lorem.sentence(6), + description: FFaker::Lorem.sentence, state: ['opened', 'closed'].sample, milestone: project.milestones.sample, assignee: project.team.users.sample diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index f9b2fd8b05f..0825776ffaa 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -10,8 +10,8 @@ Gitlab::Seeder.quiet do params = { source_branch: source_branch, target_branch: target_branch, - title: Faker::Lorem.sentence(6), - description: Faker::Lorem.sentences(3).join(" "), + title: FFaker::Lorem.sentence(6), + description: FFaker::Lorem.sentences(3).join(" "), milestone: project.milestones.sample, assignee: project.team.users.sample } diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb index b3a6f39c7d5..3bd4b442ade 100644 --- a/db/fixtures/development/12_snippets.rb +++ b/db/fixtures/development/12_snippets.rb @@ -28,8 +28,8 @@ eos PersonalSnippet.seed(:id, [{ id: i, author_id: user.id, - title: Faker::Lorem.sentence(3), - file_name: Faker::Internet.domain_word + '.rb', + title: FFaker::Lorem.sentence(3), + file_name: FFaker::Internet.domain_word + '.rb', visibility_level: Gitlab::VisibilityLevel.values.sample, content: content, }]) diff --git a/db/fixtures/development/13_comments.rb b/db/fixtures/development/13_comments.rb index d37be53c7b9..566c0705638 100644 --- a/db/fixtures/development/13_comments.rb +++ b/db/fixtures/development/13_comments.rb @@ -6,7 +6,7 @@ Gitlab::Seeder.quiet do note_params = { noteable_type: 'Issue', noteable_id: issue.id, - note: Faker::Lorem.sentence, + note: FFaker::Lorem.sentence, } Notes::CreateService.new(project, user, note_params).execute @@ -21,7 +21,7 @@ Gitlab::Seeder.quiet do note_params = { noteable_type: 'MergeRequest', noteable_id: mr.id, - note: Faker::Lorem.sentence, + note: FFaker::Lorem.sentence, } Notes::CreateService.new(project, user, note_params).execute diff --git a/db/migrate/20150406133311_add_invite_data_to_member.rb b/db/migrate/20150406133311_add_invite_data_to_member.rb index 3452fd45c4f..5d3e856ddce 100644 --- a/db/migrate/20150406133311_add_invite_data_to_member.rb +++ b/db/migrate/20150406133311_add_invite_data_to_member.rb @@ -1,5 +1,5 @@ class AddInviteDataToMember < ActiveRecord::Migration - def change + def up add_column :members, :created_by_id, :integer add_column :members, :invite_email, :string add_column :members, :invite_token, :string @@ -9,4 +9,15 @@ class AddInviteDataToMember < ActiveRecord::Migration add_index :members, :invite_token, unique: true end + + def down + remove_index :members, :invite_token + + change_column :members, :user_id, :integer, null: false + + remove_column :members, :invite_accepted_at + remove_column :members, :invite_token + remove_column :members, :invite_email + remove_column :members, :created_by_id + end end diff --git a/db/migrate/20150417122318_remove_import_data_from_project.rb b/db/migrate/20150417122318_remove_import_data_from_project.rb index c275b49d228..46cf63593c9 100644 --- a/db/migrate/20150417122318_remove_import_data_from_project.rb +++ b/db/migrate/20150417122318_remove_import_data_from_project.rb @@ -1,5 +1,9 @@ class RemoveImportDataFromProject < ActiveRecord::Migration - def change + def up remove_column :projects, :import_data end + + def down + add_column :projects, :import_data, :text + end end diff --git a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb index 51237354d9f..8f1b0cc8935 100644 --- a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb +++ b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb @@ -1,7 +1,11 @@ class AddDefaultSnippetVisibilityToAppSettings < ActiveRecord::Migration - def change + def up add_column :application_settings, :default_snippet_visibility, :integer visibility = Settings.gitlab.default_projects_features['visibility_level'] execute("update application_settings set default_snippet_visibility = #{visibility}") end + + def down + remove_column :application_settings, :default_snippet_visibility + end end diff --git a/db/migrate/20150516060434_add_note_events_to_web_hooks.rb b/db/migrate/20150516060434_add_note_events_to_web_hooks.rb new file mode 100644 index 00000000000..0097587b4f6 --- /dev/null +++ b/db/migrate/20150516060434_add_note_events_to_web_hooks.rb @@ -0,0 +1,9 @@ +class AddNoteEventsToWebHooks < ActiveRecord::Migration + def up + add_column :web_hooks, :note_events, :boolean, default: false, null: false + end + + def down + remove_column :web_hooks, :note_events, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index f7581eaf7fb..1ab91256406 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: 20150509180749) do +ActiveRecord::Schema.define(version: 20150516060434) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -533,6 +533,7 @@ ActiveRecord::Schema.define(version: 20150509180749) do t.boolean "issues_events", default: false, null: false t.boolean "merge_requests_events", default: false, null: false t.boolean "tag_push_events", default: false + t.boolean "note_events", default: false, null: false end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree diff --git a/doc/development/README.md b/doc/development/README.md index 16df0b40c47..6bc8e1888db 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -7,3 +7,4 @@ - [Sidekiq debugging](sidekiq_debugging.md) - [UI guide](ui_guide.md) for building GitLab with existing css styles and elements - [Migration Style Guide](migration_style_guide.md) for creating safe migrations +- [How to dump production data to staging](dump_db.md) diff --git a/doc/development/db_dump.md b/doc/development/db_dump.md new file mode 100644 index 00000000000..4ad3bd534e0 --- /dev/null +++ b/doc/development/db_dump.md @@ -0,0 +1,45 @@ +# Importing a database dump into a staging enviroment + +Sometimes it is useful to import the database from a production environment +into a staging environment for testing. The procedure below assumes you have +SSH+sudo access to both the production environment and the staging VM. + +On the staging VM, add the following line to `/etc/gitlab/gitlab.rb` to speed up +large database imports. + +``` +# On STAGING +echo "postgresql['checkpoint_segments'] = 64" | sudo tee -a /etc/gitlab/gitlab.rb +sudo touch /etc/gitlab/skip-auto-migrations +sudo gitlab-ctl reconfigure +``` + +Next, we let the production environment stream a compressed SQL dump to our +local machine via SSH, and redirect this stream to a psql client on the staging +VM. + +``` +# On LOCAL MACHINE +ssh -C gitlab.example.com sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -Cc gitlabhq_production |\ + ssh -C staging-vm sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -d template1 +``` + +## Recreating directory structure + +If you need to re-create some directory structure on the staging server you can +use this procedure. + +First, on the production server, create a list of directories you want to +re-create. + +``` +# On PRODUCTION +(umask 077; sudo find /var/opt/gitlab/git-data/repositories -maxdepth 1 -type d -print0 > directories.txt) +``` + +Copy `directories.txt` to the staging server and create the directories there. + +``` +# On STAGING +sudo -u git xargs -0 mkdir -p < directories.txt +``` diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index 30c29084e34..9c7f723c06d 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -66,16 +66,26 @@ It is not reasonable to italicize just _part_ of a word, especially when you're perform_complicated_task do_this_and_do_that_and_another_thing -perform_complicated_task +perform_complicated_task do_this_and_do_that_and_another_thing ## URL auto-linking -GFM will autolink standard URLs you copy and paste into your text. So if you want to link to a URL (instead of a textural link), you can simply put the URL in verbatim and it will be turned into a link to that URL. - - http://www.google.com - -http://www.google.com +GFM will autolink almost any URL you copy and paste into your text. + + * http://www.google.com + * https://google.com/ + * ftp://ftp.us.debian.org/debian/ + * smb://foo/bar/baz + * irc://irc.freenode.net/gitlab + * http://localhost:3000 + +* http://www.google.com +* https://google.com/ +* ftp://ftp.us.debian.org/debian/ +* smb://foo/bar/baz +* irc://irc.freenode.net/gitlab +* http://localhost:3000 ## Code and Syntax Highlighting diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index bca4fcfb404..ae2d465e0c1 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -299,3 +299,7 @@ Example: LVM snapshots + rsync If you are running GitLab on a virtualized server you can possibly also create VM snapshots of the entire GitLab server. It is not uncommon however for a VM snapshot to require you to power down the server, so this approach is probably of limited practical use. + +### Note +This documentation is for GitLab CE. +We backup GitLab.com and make sure your data is secure, but you can't use these methods to export / backup your data yourself from GitLab.com.
\ No newline at end of file diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 5dd495718b0..eb97f3cd7f6 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -9,7 +9,8 @@ The new release manager should create overall issue to track the progress. ## Release Manager -A release manager is selected that coordinates all releases the coming month, including the patch releases for previous releases. +A release manager is selected that coordinates all releases the coming month, +including the patch releases for previous releases. The release manager has to make sure all the steps below are done and delegated where necessary. This person should also make sure this document is kept up to date and issues are created and updated. @@ -93,6 +94,8 @@ There are three changelogs that need to be updated: CE, EE and CI. Once the stable branches have been created, update the CHANGELOG in `master` with the upcoming version, usually X.X.X.pre. +On creating the stable branches, notify the core team and developers. + ## QA Create issue on dev.gitlab.org `gitlab` repository, named "GitLab X.X QA" in order to keep track of the progress. diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index d140f3a457a..73717ffc7d6 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -140,6 +140,285 @@ X-Gitlab-Event: Issue Hook } } ``` +## Comment events + +Triggered when a new comment is made on commits, merge requests, issues, and code snippets. +The note data will be stored in `object_attributes` (e.g. `note`, `noteable_type`). The +payload will also include information about the target of the comment. For example, +a comment on a issue will include the specific issue information under the `issue` key. +Valid target types: + +1. `commit` +2. `merge_request` +3. `issue` +4. `snippet` + +### Comment on commit + +**Request header**: + +``` +X-Gitlab-Event: Note Hook +``` + +**Request body:** + +```json +{ + "object_kind": "note", + "user": { + "name": "Adminstrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "repository": { + "name": "Gitlab Test", + "url": "http://localhost/gitlab-org/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlab-org/gitlab-test" + }, + "object_attributes": { + "id": 1243, + "note": "This is a commit comment. How does this work?", + "noteable_type": "Commit", + "author_id": 1, + "created_at": "2015-05-17 18:08:09 UTC", + "updated_at": "2015-05-17 18:08:09 UTC", + "project_id": 5, + "attachment":null, + "line_code": "bec9703f7a456cd2b4ab5fb3220ae016e3e394e3_0_1", + "commit_id": "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", + "noteable_id": null, + "system": false, + "st_diff": { + "diff": "--- /dev/null\n+++ b/six\n@@ -0,0 +1 @@\n+Subproject commit 409f37c4f05865e4fb208c771485f211a22c4c2d\n", + "new_path": "six", + "old_path": "six", + "a_mode": "0", + "b_mode": "160000", + "new_file": true, + "renamed_file": false, + "deleted_file": false + }, + "url": "http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660#note_1243" + }, + "commit": { + "id": "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", + "message": "Add submodule\n\nSigned-off-by: Dmitriy Zaporozhets \u003cdmitriy.zaporozhets@gmail.com\u003e\n", + "timestamp": "2014-02-27T10:06:20+02:00", + "url": "http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660", + "author": { + "name": "Dmitriy Zaporozhets", + "email": "dmitriy.zaporozhets@gmail.com" + } + } +} +``` + +### Comment on merge request + +**Request header**: + +``` +X-Gitlab-Event: Note Hook +``` + +**Request body:** + +```json +{ + "object_kind": "note", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "repository": { + "name": "Gitlab Test", + "url": "http://example.com/gitlab-org/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlab-org/gitlab-test" + }, + "object_attributes": { + "id": 1244, + "note": "This MR needs work.", + "noteable_type": "MergeRequest", + "author_id": 1, + "created_at": "2015-05-17 18:21:36 UTC", + "updated_at": "2015-05-17 18:21:36 UTC", + "project_id": 5, + "attachment": null, + "line_code": null, + "commit_id": "", + "noteable_id": 7, + "system": false, + "st_diff": null, + "url": "http://example.com/gitlab-org/gitlab-test/merge_requests/1#note_1244" + }, + "merge_request": { + "id": 7, + "target_branch": "markdown", + "source_branch": "master", + "source_project_id": 5, + "author_id": 8, + "assignee_id": 28, + "title": "Tempora et eos debitis quae laborum et.", + "created_at": "2015-03-01 20:12:53 UTC", + "updated_at": "2015-03-21 18:27:27 UTC", + "milestone_id": 11, + "state": "opened", + "merge_status": "cannot_be_merged", + "target_project_id": 5, + "iid": 1, + "description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.", + "position": 0, + "locked_at": null, + "source": { + "name": "Gitlab Test", + "ssh_url": "git@example.com:gitlab-org/gitlab-test.git", + "http_url": "http://example.com/gitlab-org/gitlab-test.git", + "namespace": "Gitlab Org", + "visibility_level": 10 + }, + "target": { + "name": "Gitlab Test", + "ssh_url": "git@example.com:gitlab-org/gitlab-test.git", + "http_url": "http://example.com/gitlab-org/gitlab-test.git", + "namespace": "Gitlab Org", + "visibility_level": 10 + }, + "last_commit": { + "id": "562e173be03b8ff2efb05345d12df18815438a4b", + "message": "Merge branch 'another-branch' into 'master'\n\nCheck in this test\n", + "timestamp": "2015-04-08T21: 00:25-07:00", + "url": "http://example.com/gitlab-org/gitlab-test/commit/562e173be03b8ff2efb05345d12df18815438a4b", + "author": { + "name": "John Smith", + "email": "john@example.com" + } + } + } +} +``` + +### Comment on issue + +**Request header**: + +``` +X-Gitlab-Event: Note Hook +``` + +**Request body:** + +```json +{ + "object_kind": "note", + "user": { + "name": "Adminstrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "repository": { + "name": "Gitlab Test", + "url": "http://example.com/gitlab-org/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlab-org/gitlab-test" + }, + "object_attributes": { + "id": 1241, + "note": "Hello world", + "noteable_type": "Issue", + "author_id": 1, + "created_at": "2015-05-17 17:06:40 UTC", + "updated_at": "2015-05-17 17:06:40 UTC", + "project_id": 5, + "attachment": null, + "line_code": null, + "commit_id": "", + "noteable_id": 92, + "system": false, + "st_diff": null, + "url": "http://example.com/gitlab-org/gitlab-test/issues/17#note_1241" + }, + "issue": { + "id": 92, + "title": "test", + "assignee_id": null, + "author_id": 1, + "project_id": 5, + "created_at": "2015-04-12 14:53:17 UTC", + "updated_at": "2015-04-26 08:28:42 UTC", + "position": 0, + "branch_name": null, + "description": "test", + "milestone_id": null, + "state": "closed", + "iid": 17 + } +} +``` + +### Comment on code snippet + + +**Request header**: + +``` +X-Gitlab-Event: Note Hook +``` + +**Request body:** + +``` +{ + "object_kind": "note", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "repository": { + "name": "Gitlab Test", + "url": "http://example.com/gitlab-org/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlab-org/gitlab-test" + }, + "object_attributes": { + "id": 1245, + "note": "Is this snippet doing what it's supposed to be doing?", + "noteable_type": "Snippet", + "author_id": 1, + "created_at": "2015-05-17 18:35:50 UTC", + "updated_at": "2015-05-17 18:35:50 UTC", + "project_id": 5, + "attachment": null, + "line_code": null, + "commit_id": "", + "noteable_id": 53, + "system": false, + "st_diff": null, + "url": "http://example.com/gitlab-org/gitlab-test/snippets/53#note_1245" + }, + "snippet": { + "id": 53, + "title": "test", + "content": "puts 'Hello world'", + "author_id": 1, + "project_id": 5, + "created_at": "2015-04-09 02:40:38 UTC", + "updated_at": "2015-04-09 02:40:38 UTC", + "file_name": "test.rb", + "expires_at": null, + "type": "ProjectSnippet", + "visibility_level": 0 + } +} +``` ## Merge request events diff --git a/doc/workflow/README.md b/doc/workflow/README.md index 7e996dc47d4..0fca68f364e 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -12,4 +12,6 @@ - [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) +- [Change your time zone](timezone.md) +- [Keyboard shortcuts](shortcuts.md) +- [Web Editor](web_editor.md)
\ No newline at end of file diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md new file mode 100644 index 00000000000..ffcb832cdd7 --- /dev/null +++ b/doc/workflow/shortcuts.md @@ -0,0 +1,5 @@ +# GitLab keyboard shortcuts + +You can see GitLab's keyboard shortcuts by using 'shift + ?' + +
\ No newline at end of file diff --git a/doc/workflow/shortcuts.png b/doc/workflow/shortcuts.png Binary files differnew file mode 100644 index 00000000000..68756ed1f98 --- /dev/null +++ b/doc/workflow/shortcuts.png diff --git a/doc/workflow/timezone.md b/doc/workflow/timezone.md new file mode 100644 index 00000000000..8540ccfcabb --- /dev/null +++ b/doc/workflow/timezone.md @@ -0,0 +1,18 @@ +# Changing your time zone + +GitLab defaults its time zone to UTC. It has a global timezone configuration parameter in /etc/gitlab/gitlab.rb + +To update, add the time zone that best applies to your location. Here are two examples: +``` +gitlab_rails['time_zone'] = 'America/New_York' +``` +or +``` +gitlab_rails['time_zone'] = 'Europe/Brussels' +``` + +After you added this field, reconfigure and restart: +``` +gitlab-ctl reconfigure +gitlab-ctl restart +```
\ No newline at end of file diff --git a/doc_styleguide.md b/doc_styleguide.md new file mode 100644 index 00000000000..670af765f3a --- /dev/null +++ b/doc_styleguide.md @@ -0,0 +1,22 @@ +# Documentation styleguide + +This styleguide recommends best practices to improve documentation and to keep it organized and easy to find. + +## Text + +* Make sure that the documentation is added in the correct directory and that there's a link to it somewhere useful. + +* Add only one H1 or title in each document, by adding '#' at the begining of it (when using markdown). For subtitles, use '##', '###' and so on. + +* Do not duplicate information. + +* Be brief and clear. + + +## When adding images to a document + +* Create a directory to store the images with the specific name of the document where the images belong. It could be in the same directory where the .md document that you're working on is located. + +* Images should have a specific, non-generic name that will differentiate them. + +* Keep all file names in lower case.
\ No newline at end of file diff --git a/docker/single/Dockerfile b/docker/single/Dockerfile index 89224572534..a6cbf131237 100644 --- a/docker/single/Dockerfile +++ b/docker/single/Dockerfile @@ -28,6 +28,7 @@ EXPOSE 80 22 # Copy assets COPY assets/wrapper /usr/local/bin/ +COPY assets/gitlab.rb /etc/gitlab/ # Wrapper to handle signal, trigger runit and reconfigure GitLab CMD ["/usr/local/bin/wrapper"] diff --git a/docker/single/assets/gitlab.rb b/docker/single/assets/gitlab.rb new file mode 100644 index 00000000000..ef84e7832d6 --- /dev/null +++ b/docker/single/assets/gitlab.rb @@ -0,0 +1,37 @@ +# External URL should be your Docker instance. +# By default, GitLab will use the Docker container hostname. +# Always use port 80 here to force the internal nginx to bind port 80, +# even if you intend to use another port in Docker. +# external_url "http://192.168.59.103/" + +# Prevent Postgres from trying to allocate 25% of total memory +postgresql['shared_buffers'] = '1MB' + +# Configure GitLab to redirect PostgreSQL logs to the data volume +postgresql['log_directory'] = '/var/log/gitlab/postgresql' + +# Some configuration of GitLab +# You can find more at https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#configuration +gitlab_rails['gitlab_email_from'] = 'gitlab@example.com' +gitlab_rails['gitlab_support_email'] = 'support@example.com' +gitlab_rails['time_zone'] = 'Europe/Paris' + +# SMTP settings +# You must use an external server, the Docker container does not install an SMTP server +gitlab_rails['smtp_enable'] = true +gitlab_rails['smtp_address'] = "smtp.example.com" +gitlab_rails['smtp_port'] = 587 +gitlab_rails['smtp_user_name'] = "user" +gitlab_rails['smtp_password'] = "password" +gitlab_rails['smtp_domain'] = "example.com" +gitlab_rails['smtp_authentication'] = "plain" +gitlab_rails['smtp_enable_starttls_auto'] = true + +# Enable LDAP authentication +# gitlab_rails['ldap_enabled'] = true +# gitlab_rails['ldap_host'] = 'ldap.example.com' +# gitlab_rails['ldap_port'] = 389 +# gitlab_rails['ldap_method'] = 'plain' # 'ssl' or 'plain' +# gitlab_rails['ldap_allow_username_or_email_login'] = false +# gitlab_rails['ldap_uid'] = 'uid' +# gitlab_rails['ldap_base'] = 'ou=users,dc=example,dc=com' diff --git a/features/project/forked_merge_requests.feature b/features/project/forked_merge_requests.feature index d9fbb875c28..ad1160e3343 100644 --- a/features/project/forked_merge_requests.feature +++ b/features/project/forked_merge_requests.feature @@ -38,3 +38,15 @@ Feature: Project Forked Merge Requests Given I visit project "Forked Shop" merge requests page And I click link "New Merge Request" Then the target repository should be the original repository + + @javascript + Scenario: I see the users in the target project for a new merge request + Given I logout + And I sign in as an admin + And I have a project forked off of "Shop" called "Forked Shop" + Then I visit project "Forked Shop" merge requests page + And I click link "New Merge Request" + And I fill out a "Merge Request On Forked Project" merge request + When I click "Assign to" dropdown" + Then I should see the target project ID in the input selector + And I should see the users from the target project ID diff --git a/features/project/project.feature b/features/project/project.feature index ae28312a69a..ef11bceed11 100644 --- a/features/project/project.feature +++ b/features/project/project.feature @@ -62,3 +62,9 @@ Feature: Project And I add project tags And I save project Then I should see project tags + + Scenario: I should not see "New Issue" or "New Merge Request" buttons + Given I disable issues and merge requests in project + When I visit project "Shop" page + Then I should not see "New Issue" button + And I should not see "New Merge Request" button diff --git a/features/project/wiki.feature b/features/project/wiki.feature index 977cd609a11..7a70f348754 100644 --- a/features/project/wiki.feature +++ b/features/project/wiki.feature @@ -69,6 +69,11 @@ Feature: Project Wiki And I click on the "Pages" button Then I should see non-escaped link in the pages list + @javascript @focus + Scenario: Creating an invalid new page + Given I create a New page with an invalid name + Then I should see an error message + @javascript Scenario: Edit Wiki page that has a path Given I create a New page with paths diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 94d21d28a0c..ebfa102cee5 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -128,6 +128,21 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps page.should have_select("merge_request_target_project_id", selected: @project.path_with_namespace) end + step 'I click "Assign to" dropdown"' do + first('.ajax-users-select').click + end + + step 'I should see the target project ID in the input selector' do + expect(page).to have_selector("input[data-project-id=\"#{@project.id}\"]") + end + + step 'I should see the users from the target project ID' do + expect(page).to have_selector('.user-result', visible: true, count: 2) + users = page.all('.user-name') + users[0].text.should == 'Unassigned' + users[1].text.should == @project.users.first.name + end + # Verify a link is generated against the correct project def verify_commit_link(container_div, container_project) # This should force a wait for the javascript to execute diff --git a/features/steps/project/hooks.rb b/features/steps/project/hooks.rb index 4b135202593..d06905285fe 100644 --- a/features/steps/project/hooks.rb +++ b/features/steps/project/hooks.rb @@ -23,7 +23,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps end step 'I submit new hook' do - @url = Faker::Internet.uri("http") + @url = FFaker::Internet.uri("http") fill_in "hook_url", with: @url expect { click_button "Add Web Hook" }.to change(ProjectHook, :count).by(1) end diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index 00706ab30e9..93fea693f89 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -102,4 +102,12 @@ class Spinach::Features::Project < Spinach::FeatureSteps step 'I should see project tags' do expect(find_field('Tags').value).to eq 'tag1, tag2' end + + step 'I should not see "New Issue" button' do + page.should_not have_link 'New Issue' + end + + step 'I should not see "New Merge Request" button' do + page.should_not have_link 'New Merge Request' + end end diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index 717132da45d..58cb0ceb3f1 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -133,6 +133,16 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps current_path.should include 'one/two/three' end + step 'I create a New page with an invalid name' do + click_on 'New Page' + fill_in 'Page slug', with: 'invalid name' + click_on 'Build' + end + + step 'I should see an error message' do + expect(page).to have_content "The page slug is invalid" + end + step 'I should see non-escaped link in the pages list' do page.should have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three']") end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index b60ac5e3423..24136fe421c 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -14,6 +14,12 @@ module SharedProject @project.team << [@user, :master] end + step 'I disable issues and merge requests in project' do + @project.issues_enabled = false + @project.merge_requests_enabled = false + @project.save + end + # Add another user to project "Shop" step 'I add a user to project "Shop"' do @project = Project.find_by(name: "Shop") diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 85e9081680d..1ebf9a1f022 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -243,7 +243,7 @@ module API end def secret_token - File.read(Rails.root.join('.gitlab_shell_secret')).chomp + File.read(Gitlab.config.gitlab_shell.secret_file).chomp end def handle_member_errors(errors) diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index be9850367b9..ad4d2e65dfd 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -43,7 +43,8 @@ module API :push_events, :issues_events, :merge_requests_events, - :tag_push_events + :tag_push_events, + :note_events ] @hook = user_project.hooks.new(attrs) @@ -73,7 +74,8 @@ module API :push_events, :issues_events, :merge_requests_events, - :tag_push_events + :tag_push_events, + :note_events ] if @hook.update_attributes attrs diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb new file mode 100644 index 00000000000..bf33e5b1b1e --- /dev/null +++ b/lib/gitlab/asciidoc.rb @@ -0,0 +1,60 @@ +require 'asciidoctor' +require 'html/pipeline' + +module Gitlab + # Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters + # the resulting HTML through HTML pipeline filters. + module Asciidoc + + # Provide autoload paths for filters to prevent a circular dependency error + autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' + + DEFAULT_ADOC_ATTRS = [ + 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', + 'env-gitlab', 'source-highlighter=html-pipeline' + ].freeze + + # Public: Converts the provided Asciidoc markup into HTML. + # + # input - the source text in Asciidoc format + # context - a Hash with the template context: + # :commit + # :project + # :project_wiki + # :requested_path + # :ref + # asciidoc_opts - a Hash of options to pass to the Asciidoctor converter + # html_opts - a Hash of options for HTML output: + # :xhtml - output XHTML instead of HTML + # + def self.render(input, context, asciidoc_opts = {}, html_opts = {}) + asciidoc_opts = asciidoc_opts.reverse_merge( + safe: :secure, + backend: html_opts[:xhtml] ? :xhtml5 : :html5, + attributes: [] + ) + asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS) + + html = ::Asciidoctor.convert(input, asciidoc_opts) + + if context[:project] + result = HTML::Pipeline.new(filters).call(html, context) + + save_opts = html_opts[:xhtml] ? + Nokogiri::XML::Node::SaveOptions::AS_XHTML : 0 + + html = result[:output].to_html(save_with: save_opts) + end + + html.html_safe + end + + private + + def self.filters + [ + Gitlab::Markdown::RelativeLinkFilter + ] + end + end +end diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 050b5ba29dd..03cef30c97d 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -1,4 +1,3 @@ -require_relative 'rack_attack_helpers' require_relative 'shell_env' module Grack diff --git a/lib/gitlab/backend/rack_attack_helpers.rb b/lib/gitlab/backend/rack_attack_helpers.rb deleted file mode 100644 index 8538f3f6eca..00000000000 --- a/lib/gitlab/backend/rack_attack_helpers.rb +++ /dev/null @@ -1,31 +0,0 @@ -# rack-attack v4.2.0 doesn't yet support clearing of keys. -# Taken from https://github.com/kickstarter/rack-attack/issues/113 -class Rack::Attack::Allow2Ban - def self.reset(discriminator, options) - findtime = options[:findtime] or raise ArgumentError, "Must pass findtime option" - - cache.reset_count("#{key_prefix}:count:#{discriminator}", findtime) - cache.delete("#{key_prefix}:ban:#{discriminator}") - end -end - -class Rack::Attack::Cache - def reset_count(unprefixed_key, period) - epoch_time = Time.now.to_i - # Add 1 to expires_in to avoid timing error: http://git.io/i1PHXA - expires_in = period - (epoch_time % period) + 1 - key = "#{(epoch_time / period).to_i}:#{unprefixed_key}" - delete(key) - end - - def delete(unprefixed_key) - store.delete("#{prefix}:#{unprefixed_key}") - end -end - -class Rack::Attack::StoreProxy::RedisStoreProxy - def delete(key, options={}) - self.del(key) - rescue Redis::BaseError - end -end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 133010adcaf..c0fb22e7f36 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -1,5 +1,4 @@ require 'html/pipeline' -require 'task_list/filter' module Gitlab # Custom parser for GitLab-flavored Markdown @@ -19,6 +18,7 @@ module Gitlab autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter' autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter' autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter' + autoload :TaskListFilter, 'gitlab/markdown/task_list_filter' autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' # Public: Parse the provided text with GitLab-Flavored Markdown @@ -113,7 +113,7 @@ module Gitlab Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::LabelReferenceFilter, - TaskList::Filter + Gitlab::Markdown::TaskListFilter ] end end diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index a357f28458d..1a77becee89 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -84,11 +84,11 @@ module Gitlab # # Returns a Hash. def label_params(id, name) - if id > 0 - { id: id } - else + if name # TODO (rspeicher): Don't strip single quotes if we decide to only use double quotes for surrounding. { name: name.tr('\'"', '') } + else + { id: id } end end end diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb index 6f33155badf..88781fea0c8 100644 --- a/lib/gitlab/markdown/sanitization_filter.rb +++ b/lib/gitlab/markdown/sanitization_filter.rb @@ -8,28 +8,33 @@ module Gitlab # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist. class SanitizationFilter < HTML::Pipeline::SanitizationFilter def whitelist - whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST + whitelist = super - # Allow code highlighting - whitelist[:attributes]['pre'] = %w(class) - whitelist[:attributes]['span'] = %w(class) + # Only push these customizations once + unless customized?(whitelist[:transformers]) + # Allow code highlighting + whitelist[:attributes]['pre'] = %w(class) + whitelist[:attributes]['span'] = %w(class) - # Allow table alignment - whitelist[:attributes]['th'] = %w(style) - whitelist[:attributes]['td'] = %w(style) + # Allow table alignment + whitelist[:attributes]['th'] = %w(style) + whitelist[:attributes]['td'] = %w(style) - # Allow span elements - whitelist[:elements].push('span') + # Allow span elements + whitelist[:elements].push('span') - # Remove `rel` attribute from `a` elements - whitelist[:transformers].push(remove_rel) + # Remove `rel` attribute from `a` elements + whitelist[:transformers].push(remove_rel) - # Remove `class` attribute from non-highlight spans - whitelist[:transformers].push(clean_spans) + # Remove `class` attribute from non-highlight spans + whitelist[:transformers].push(clean_spans) + end whitelist end + private + def remove_rel lambda do |env| if env[:node_name] == 'a' @@ -48,6 +53,10 @@ module Gitlab end end end + + def customized?(transformers) + transformers.last.source_location[0] == __FILE__ + end end end end diff --git a/lib/gitlab/markdown/task_list_filter.rb b/lib/gitlab/markdown/task_list_filter.rb new file mode 100644 index 00000000000..c6eb2e2bf6d --- /dev/null +++ b/lib/gitlab/markdown/task_list_filter.rb @@ -0,0 +1,23 @@ +require 'task_list/filter' + +module Gitlab + module Markdown + # Work around a bug in the default TaskList::Filter that adds a `task-list` + # class to every list element, regardless of whether or not it contains a + # task list. + # + # This is a (hopefully) temporary fix, pending a new release of the + # task_list gem. + # + # See https://github.com/github/task_list/pull/60 + class TaskListFilter < TaskList::Filter + def add_css_class(node, *new_class_names) + if new_class_names.include?('task-list') + super if node.children.any? { |c| c['class'] == 'task-list-item' } + else + super + end + end + end + end +end diff --git a/lib/gitlab/markdown_helper.rb b/lib/gitlab/markup_helper.rb index 5e3cfc0585b..f99be969d3e 100644 --- a/lib/gitlab/markdown_helper.rb +++ b/lib/gitlab/markup_helper.rb @@ -1,5 +1,5 @@ module Gitlab - module MarkdownHelper + module MarkupHelper module_function # Public: Determines if a given filename is compatible with GitHub::Markup. @@ -8,8 +8,10 @@ module Gitlab # # Returns boolean def markup?(filename) - filename.downcase.end_with?(*%w(.textile .rdoc .org .creole .wiki - .mediawiki .rst .adoc .asciidoc .asc)) + gitlab_markdown?(filename) || + asciidoc?(filename) || + filename.downcase.end_with?(*%w(.textile .rdoc .org .creole .wiki + .mediawiki .rst)) end # Public: Determines if a given filename is compatible with @@ -22,8 +24,17 @@ module Gitlab filename.downcase.end_with?(*%w(.mdown .md .markdown)) end + # Public: Determines if the given filename has AsciiDoc extension. + # + # filename - Filename string to check + # + # Returns boolean + def asciidoc?(filename) + filename.downcase.end_with?(*%w(.adoc .ad .asciidoc)) + end + def previewable?(filename) - gitlab_markdown?(filename) || markup?(filename) + markup?(filename) end end end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 75a3dfe37c3..06245374bc8 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -51,11 +51,23 @@ module Gitlab end def issues - Issue.where(project_id: limit_project_ids).full_search(query).order('updated_at DESC') + issues = Issue.where(project_id: limit_project_ids) + if query =~ /#(\d+)\z/ + issues = issues.where(iid: $1) + else + issues = issues.full_search(query) + end + issues.order('updated_at DESC') end def merge_requests - MergeRequest.in_projects(limit_project_ids).full_search(query).order('updated_at DESC') + merge_requests = MergeRequest.in_projects(limit_project_ids) + if query =~ /[#!](\d+)\z/ + merge_requests = merge_requests.where(iid: $1) + else + merge_requests = merge_requests.full_search(query) + end + merge_requests.order('updated_at DESC') end def default_scope diff --git a/spec/factories.rb b/spec/factories.rb index 26e8a795fa4..b7b2a1dac8e 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -2,23 +2,23 @@ include ActionDispatch::TestProcess FactoryGirl.define do sequence :sentence, aliases: [:title, :content] do - Faker::Lorem.sentence + FFaker::Lorem.sentence end sequence :name do - Faker::Name.name + FFaker::Name.name end sequence :file_name do - Faker::Internet.user_name + FFaker::Internet.user_name end - sequence(:url) { Faker::Internet.uri('http') } + sequence(:url) { FFaker::Internet.uri('http') } factory :user, aliases: [:author, :assignee, :owner, :creator] do - email { Faker::Internet.email } + email { FFaker::Internet.email } name - sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" } + sequence(:username) { |n| "#{FFaker::Internet.user_name}#{n}" } password "12345678" confirmed_at { Time.now } confirmation_token { nil } @@ -122,12 +122,12 @@ FactoryGirl.define do factory :email do user email do - Faker::Internet.email('alias') + FFaker::Internet.email('alias') end factory :another_email do email do - Faker::Internet.email('another.alias') + FFaker::Internet.email('another.alias') end end end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index 25862614d28..00906e8087a 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -26,7 +26,7 @@ describe "Admin::Hooks", feature: true do describe "New Hook" do before do - @url = Faker::Internet.uri("http") + @url = FFaker::Internet.uri("http") visit admin_hooks_path fill_in "hook_url", with: @url expect { click_button "Add System Hook" }.to change(SystemHook, :count).by(1) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index d4cf6540080..3307ac776fc 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -261,12 +261,26 @@ describe ApplicationHelper do end end - describe 'markup_render' do + describe 'render_markup' do let(:content) { 'Noël' } it 'should preserve encoding' do expect(content.encoding.name).to eq('UTF-8') expect(render_markup('foo.rst', content).encoding.name).to eq('UTF-8') end + + it "should delegate to #markdown when file name corresponds to Markdown" do + expect(self).to receive(:gitlab_markdown?).with('foo.md').and_return(true) + expect(self).to receive(:markdown).and_return('NOEL') + + expect(render_markup('foo.md', content)).to eq('NOEL') + end + + it "should delegate to #asciidoc when file name corresponds to AsciiDoc" do + expect(self).to receive(:asciidoc?).with('foo.adoc').and_return(true) + expect(self).to receive(:asciidoc).and_return('NOEL') + + expect(render_markup('foo.adoc', content)).to eq('NOEL') + end end end diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index dd4c1d645e2..e0be2df0e5e 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -106,6 +106,16 @@ describe DiffHelper do end end + describe 'unfold_class' do + it 'returns empty on false' do + expect(unfold_class(false)).to eq('') + end + + it 'returns a class on true' do + expect(unfold_class(true)).to eq('unfold js-unfold') + end + end + describe 'diff_line_content' do it 'should return non breaking space when line is empty' do diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb new file mode 100644 index 00000000000..7a3e38d7e63 --- /dev/null +++ b/spec/helpers/emails_helper_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe EmailsHelper do + describe 'password_reset_token_valid_time' do + def validate_time_string(time_limit, expected_string) + Devise.reset_password_within = time_limit + expect(password_reset_token_valid_time).to eq(expected_string) + end + + context 'when time limit is less than 2 hours' do + it 'should display the time in hours using a singular unit' do + validate_time_string(1.hour, '1 hour') + end + end + + context 'when time limit is 2 or more hours' do + it 'should display the time in hours using a plural unit' do + validate_time_string(2.hours, '2 hours') + end + end + + context 'when time limit contains fractions of an hour' do + it 'should round down to the nearest hour' do + validate_time_string(96.minutes, '1 hour') + end + end + + context 'when time limit is 24 or more hours' do + it 'should display the time in days using a singular unit' do + validate_time_string(24.hours, '1 day') + end + end + + context 'when time limit is 2 or more days' do + it 'should display the time in days using a plural unit' do + validate_time_string(2.days, '2 days') + end + end + + context 'when time limit contains fractions of a day' do + it 'should round down to the nearest day' do + validate_time_string(57.hours, '2 days') + end + end + end +end diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 9f3e8cf585e..0d0418f84a7 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -110,6 +110,14 @@ describe GitlabMarkdownHelper do helper.render_wiki_content(@wiki) end + it "should use Asciidoctor for asciidoc files" do + allow(@wiki).to receive(:format).and_return(:asciidoc) + + expect(helper).to receive(:asciidoc).with('wiki content') + + helper.render_wiki_content(@wiki) + end + it "should use the Gollum renderer for all other file types" do allow(@wiki).to receive(:format).and_return(:rdoc) formatted_content_stub = double('formatted_content') diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index 0b7e3b1d11f..0c8d06b7059 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -1,6 +1,70 @@ require 'spec_helper' describe LabelsHelper do - it { expect(text_color_for_bg('#EEEEEE')).to eq('#333333') } - it { expect(text_color_for_bg('#222E2E')).to eq('#FFFFFF') } + describe 'link_to_label' do + let(:project) { create(:empty_project) } + let(:label) { create(:label, project: project) } + + context 'with @project set' do + before do + @project = project + end + + it 'uses the instance variable' do + expect(label).not_to receive(:project) + link_to_label(label) + end + end + + context 'without @project set' do + it "uses the label's project" do + expect(label).to receive(:project).and_return(project) + link_to_label(label) + end + end + + context 'with a named project argument' do + it 'uses the provided project' do + arg = double('project') + expect(arg).to receive(:namespace).and_return('foo') + expect(arg).to receive(:to_param).and_return('foo') + + link_to_label(label, project: arg) + end + + it 'takes precedence over other types' do + @project = project + expect(@project).not_to receive(:namespace) + expect(label).not_to receive(:project) + + arg = double('project', namespace: 'foo', to_param: 'foo') + link_to_label(label, project: arg) + end + end + + context 'with block' do + it 'passes the block to link_to' do + link = link_to_label(label) { 'Foo' } + expect(link).to match('Foo') + end + end + + context 'without block' do + it 'uses render_colored_label as the link content' do + expect(self).to receive(:render_colored_label). + with(label).and_return('Foo') + expect(link_to_label(label)).to match('Foo') + end + end + end + + describe 'text_color_for_bg' do + it 'uses light text on dark backgrounds' do + expect(text_color_for_bg('#222E2E')).to eq('#FFFFFF') + end + + it 'uses dark text on light backgrounds' do + expect(text_color_for_bg('#EEEEEE')).to eq('#333333') + end + end end diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb new file mode 100644 index 00000000000..23f83339ec5 --- /dev/null +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' +require 'nokogiri' + +module Gitlab + describe Asciidoc do + + let(:input) { '<b>ascii</b>' } + let(:context) { {} } + let(:html) { 'H<sub>2</sub>O' } + + context "without project" do + + it "should convert the input using Asciidoctor and default options" do + expected_asciidoc_opts = { safe: :secure, backend: :html5, + attributes: described_class::DEFAULT_ADOC_ATTRS } + + expect(Asciidoctor).to receive(:convert) + .with(input, expected_asciidoc_opts).and_return(html) + + expect( render(input, context) ).to eql html + end + + context "with asciidoc_opts" do + + let(:asciidoc_opts) { {safe: :safe, attributes: ['foo']} } + + it "should merge the options with default ones" do + expected_asciidoc_opts = { safe: :safe, backend: :html5, + attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo'] } + + expect(Asciidoctor).to receive(:convert) + .with(input, expected_asciidoc_opts).and_return(html) + + render(input, context, asciidoc_opts) + end + end + end + + context "with project in context" do + + let(:context) { {project: create(:project)} } + + it "should filter converted input via HTML pipeline and return result" do + filtered_html = '<b>ASCII</b>' + + allow(Asciidoctor).to receive(:convert).and_return(html) + expect_any_instance_of(HTML::Pipeline).to receive(:call) + .with(html, context) + .and_return(output: Nokogiri::HTML.fragment(filtered_html)) + + expect( render('foo', context) ).to eql filtered_html + end + end + + def render(*args) + described_class.render(*args) + end + end +end diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index d0aad54f677..42c9946d2a9 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -156,7 +156,7 @@ describe Grack::Auth do end expect(attempt_login(true)).to eq(200) - expect(Rack::Attack::Allow2Ban.send(:banned?, ip)).to eq(nil) + expect(Rack::Attack::Allow2Ban.banned?(ip)).to be_falsey for n in 0..maxretry do expect(attempt_login(false)).to eq(401) diff --git a/spec/lib/gitlab/backend/rack_attack_helpers_spec.rb b/spec/lib/gitlab/backend/rack_attack_helpers_spec.rb deleted file mode 100644 index 2ac496fd669..00000000000 --- a/spec/lib/gitlab/backend/rack_attack_helpers_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require "spec_helper" - -describe 'RackAttackHelpers' do - describe 'reset' do - let(:discriminator) { 'test-key'} - let(:maxretry) { 5 } - let(:period) { 1.minute } - let(:options) { { findtime: period, bantime: 60, maxretry: maxretry } } - - def do_filter - for i in 1..maxretry - 1 do - status = Rack::Attack::Allow2Ban.filter(discriminator, options) { true } - expect(status).to eq(false) - end - end - - def do_reset - Rack::Attack::Allow2Ban.reset(discriminator, options) - end - - before do - do_reset - end - - after do - do_reset - end - - it 'user is not banned after n - 1 retries' do - do_filter - do_reset - do_filter - end - end -end diff --git a/spec/lib/gitlab/gitlab_markdown_helper_spec.rb b/spec/lib/gitlab/gitlab_markdown_helper_spec.rb deleted file mode 100644 index ab613193f41..00000000000 --- a/spec/lib/gitlab/gitlab_markdown_helper_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper' - -describe Gitlab::MarkdownHelper do - describe '#markup?' do - %w(textile rdoc org creole wiki - mediawiki rst adoc asciidoc asc).each do |type| - it "returns true for #{type} files" do - expect(Gitlab::MarkdownHelper.markup?("README.#{type}")).to be_truthy - end - end - - it 'returns false when given a non-markup filename' do - 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 - expect(Gitlab::MarkdownHelper.gitlab_markdown?("README.#{type}")).to be_truthy - end - end - - it 'returns false when given a non-markdown filename' do - expect(Gitlab::MarkdownHelper.gitlab_markdown?('README.rb')).not_to be_truthy - end - end -end diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index 9f898837466..c4548e7431f 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -149,5 +149,12 @@ module Gitlab::Markdown end end end + + describe 'edge cases' do + it 'gracefully handles non-references matching the pattern' do + exp = act = '(format nil "~0f" 3.0) ; 3.0' + expect(filter(act).to_html).to eq exp + end + end end end diff --git a/spec/lib/gitlab/markdown/task_list_filter_spec.rb b/spec/lib/gitlab/markdown/task_list_filter_spec.rb new file mode 100644 index 00000000000..2a1e1cc5127 --- /dev/null +++ b/spec/lib/gitlab/markdown/task_list_filter_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +module Gitlab::Markdown + describe TaskListFilter do + def filter(html, options = {}) + described_class.call(html, options) + end + + it 'does not apply `task-list` class to non-task lists' do + exp = act = %(<ul><li>Item</li></ul>) + expect(filter(act).to_html).to eq exp + end + end +end diff --git a/spec/lib/gitlab/markup_helper_spec.rb b/spec/lib/gitlab/markup_helper_spec.rb new file mode 100644 index 00000000000..7e716e866b1 --- /dev/null +++ b/spec/lib/gitlab/markup_helper_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe Gitlab::MarkupHelper do + describe '#markup?' do + %w(textile rdoc org creole wiki + mediawiki rst adoc ad asciidoc mdown md markdown).each do |type| + it "returns true for #{type} files" do + expect(Gitlab::MarkupHelper.markup?("README.#{type}")).to be_truthy + end + end + + it 'returns false when given a non-markup filename' do + expect(Gitlab::MarkupHelper.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 + expect(Gitlab::MarkupHelper.gitlab_markdown?("README.#{type}")).to be_truthy + end + end + + it 'returns false when given a non-markdown filename' do + expect(Gitlab::MarkupHelper.gitlab_markdown?('README.rb')).not_to be_truthy + end + end + + describe '#asciidoc?' do + %w(adoc ad asciidoc ADOC).each do |type| + it "returns true for #{type} files" do + expect(Gitlab::MarkupHelper.asciidoc?("README.#{type}")).to be_truthy + end + end + + it 'returns false when given a non-asciidoc filename' do + expect(Gitlab::MarkupHelper.asciidoc?('README.rb')).not_to be_truthy + end + end +end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index dbcf7286e45..c40ae7b5703 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -5,6 +5,8 @@ describe Notify do include EmailSpec::Matchers include RepoHelpers + new_user_address = 'newguy@example.com' + let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name } let(:gitlab_sender) { Gitlab.config.gitlab.email_from } let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to } @@ -55,18 +57,9 @@ describe Notify do end end - describe 'for new users, the email' do - let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: 'newguy@example.com', created_by_id: 1) } - - token = 'kETLwRaayvigPq_x3SNM' - - subject { Notify.new_user_email(new_user.id, token) } - - it_behaves_like 'an email sent from GitLab' - + shared_examples 'a new user email' do |user_email, site_path| it 'is sent to the new user' do - is_expected.to deliver_to new_user.email + is_expected.to deliver_to user_email end it 'has the correct subject' do @@ -74,9 +67,25 @@ describe Notify do end it 'contains the new user\'s login name' do - is_expected.to have_body_text /#{new_user.email}/ + is_expected.to have_body_text /#{user_email}/ end + it 'includes a link to the site' do + is_expected.to have_body_text /#{site_path}/ + end + end + + describe 'for new users, the email' do + let(:example_site_path) { root_path } + let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } + + token = 'kETLwRaayvigPq_x3SNM' + + subject { Notify.new_user_email(new_user.id, token) } + + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'a new user email', new_user_address + it 'contains the password text' do is_expected.to have_body_text /Click here to set your password/ end @@ -88,39 +97,26 @@ describe Notify do ) end - it 'includes a link to the site' do - is_expected.to have_body_text /#{example_site_path}/ + it 'explains the reset link expiration' do + is_expected.to have_body_text(/This link is valid for \d+ (hours?|days?)/) + is_expected.to have_body_text(new_user_password_url) + is_expected.to have_body_text(/\?user_email=.*%40.*/) end end describe 'for users that signed up, the email' do let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: 'newguy@example.com', password: "securePassword") } + let(:new_user) { create(:user, email: new_user_address, password: "securePassword") } subject { Notify.new_user_email(new_user.id) } it_behaves_like 'an email sent from GitLab' - - it 'is sent to the new user' do - is_expected.to deliver_to new_user.email - end - - it 'has the correct subject' do - is_expected.to have_subject /^Account was created for you$/i - end - - it 'contains the new user\'s login name' do - is_expected.to have_body_text /#{new_user.email}/ - end + it_behaves_like 'a new user email', new_user_address it 'should not contain the new user\'s password' do is_expected.not_to have_body_text /password/ end - - it 'includes a link to the site' do - is_expected.to have_body_text /#{example_site_path}/ - end end describe 'user added ssh key' do @@ -189,7 +185,7 @@ describe Notify do 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_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: FFaker::Lorem.sentence) } describe 'that are new' do subject { Notify.new_issue_email(issue.assignee_id, issue.id) } @@ -277,7 +273,7 @@ describe Notify do context 'for merge requests' do let(:merge_author) { create(:user) } let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) } - let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) } + let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: FFaker::Lorem.sentence) } describe 'that are new' do subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb index 4e0d50d7f3f..dae7e399cfb 100644 --- a/spec/models/hooks/project_hook_spec.rb +++ b/spec/models/hooks/project_hook_spec.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # require 'spec_helper' diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb index d9714596f5d..fb5111dd9f5 100644 --- a/spec/models/hooks/service_hook_spec.rb +++ b/spec/models/hooks/service_hook_spec.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # require "spec_helper" diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb index e4b6b886565..edb21fc2e47 100644 --- a/spec/models/hooks/system_hook_spec.rb +++ b/spec/models/hooks/system_hook_spec.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # require "spec_helper" diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 9f5ef3eff70..4c3f0cbcbbf 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -13,6 +13,7 @@ # issues_events :boolean default(FALSE), not null # merge_requests_events :boolean default(FALSE), not null # tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null # require 'spec_helper' diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 45171e1bf64..eb73aa763fc 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -47,7 +47,7 @@ describe Milestone do it "should recover from dividing by zero" do expect(milestone.issues).to receive(:count).and_return(0) - expect(milestone.percent_complete).to eq(100) + expect(milestone.percent_complete).to eq(0) end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 4c7d15d6594..8d0ae1475c2 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -5,7 +5,7 @@ describe API::API, api: true do let(:user) { create(:user) } let(:key) { create(:key, user: user) } let(:project) { create(:project) } - let(:secret_token) { File.read Rails.root.join('.gitlab_shell_secret') } + let(:secret_token) { File.read Gitlab.config.gitlab_shell.secret_file } describe "GET /internal/check", no_db: true do it do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index aada7febf6c..46cd26eb927 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -165,7 +165,7 @@ describe API::API, api: true do it "should assign attributes to project" do project = attributes_for(:project, { path: 'camelCasePath', - description: Faker::Lorem.sentence, + description: FFaker::Lorem.sentence, issues_enabled: false, merge_requests_enabled: false, wiki_enabled: false @@ -274,7 +274,7 @@ describe API::API, api: true do it 'should assign attributes to project' do project = attributes_for(:project, { - description: Faker::Lorem.sentence, + description: FFaker::Lorem.sentence, issues_enabled: false, merge_requests_enabled: false, wiki_enabled: false diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index d15dff1b52b..0e5ae724bf7 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' describe Issues::CloseService do - let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:user2) { create(:user) } let(:issue) { create(:issue, assignee: user2) } + let(:project) { issue.project } before do project.team << [user, :master] diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 22b89bec96d..6fc69e93628 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' describe Issues::UpdateService do - let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:user2) { create(:user) } let(:issue) { create(:issue) } let(:label) { create(:label) } + let(:project) { issue.project } before do project.team << [user, :master] diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 1a02299bf19..0dc3b412783 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -15,6 +15,8 @@ describe Notes::CreateService do noteable_id: issue.id } + expect(project).to receive(:execute_hooks) + expect(project).to receive(:execute_services) @note = Notes::CreateService.new(project, user, opts).execute end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 2a54b2e920a..62a99d15952 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -31,7 +31,8 @@ describe NotificationService do describe 'Notes' do context 'issue note' do - let(:issue) { create(:issue, assignee: create(:user)) } + let(:project) { create(:empty_project, :public) } + let(:issue) { create(:issue, project: project, assignee: create(:user)) } let(:mentioned_issue) { create(:issue, assignee: issue.assignee) } let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') } @@ -101,7 +102,8 @@ describe NotificationService do end context 'issue note mention' do - let(:issue) { create(:issue, assignee: create(:user)) } + let(:project) { create(:empty_project, :public) } + let(:issue) { create(:issue, project: project, assignee: create(:user)) } let(:mentioned_issue) { create(:issue, assignee: issue.assignee) } let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@all mentioned') } @@ -145,7 +147,8 @@ describe NotificationService do end context 'commit note' do - let(:note) { create(:note_on_commit) } + let(:project) { create(:project, :public) } + let(:note) { create(:note_on_commit, project: project) } before do build_team(note.project) @@ -192,7 +195,8 @@ describe NotificationService do end describe 'Issues' do - let(:issue) { create :issue, assignee: create(:user), description: 'cc @participant' } + let(:project) { create(:empty_project, :public) } + let(:issue) { create :issue, project: project, assignee: create(:user), description: 'cc @participant' } before do build_team(issue.project) @@ -295,7 +299,8 @@ describe NotificationService do end describe 'Merge Requests' do - let(:merge_request) { create :merge_request, assignee: create(:user) } + let(:project) { create(:project, :public) } + let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user) } before do build_team(merge_request.target_project) |
