diff options
98 files changed, 862 insertions, 482 deletions
diff --git a/CHANGELOG b/CHANGELOG index 2bcecb402a0..f5a53747881 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,14 +1,31 @@ Please view this file on the master branch, on stable branches it's out of date. v 7.10.0 (unreleased) + - Fix "Import projects from" button to show the correct instructions (Stan Hu) + - Fix dots in Wiki slugs causing errors (Stan Hu) + - Update poltergeist to version 1.6.0 to support PhantomJS 2.0 (Zeger-Jan van de Weg) + - Fix cross references when usernames, milestones, or project names contain underscores (Stan Hu) - Disable reference creation for comments surrounded by code/preformatted blocks (Stan Hu) - enable line wrapping per default and remove the checkbox to toggle it (Hannes Rosenögger) - extend the commit calendar to show the actual commits made on a date (Hannes Rosenögger) + - Fix a link in the patch update guide - Add a service to support external wikis (Hannes Rosenögger) - List new commits for newly pushed branch in activity view. + - Add sidetiq gem dependency to match EE - Add changelog, license and contribution guide links to project sidebar. - Improve diff UI + - Fix alignment of navbar toggle button (Cody Mize) - Identical look of selectboxes in UI + - Move "Import existing repository by URL" option to button. + - Improve error message when save profile has error. + - Passing the name of pushed ref to CI service (requires GitLab CI 7.9+) + - Add location field to user profile + - Fix print view for markdown files and wiki pages + - Improve GitLab performance when working with git repositories + - Add tag message and last commit to tag hook (Kamil Trzciński) + - Restrict permissions on backup files + - Improve oauth accounts UI in profile page + - Add ability to unlink connected accounts v 7.9.0 (unreleased) - Add HipChat integration documentation (Stan Hu) @@ -87,6 +104,8 @@ v 7.9.0 (unreleased) - Ability to unsubscribe/subscribe to issue or merge request - Delete deploy key when last connection to a project is destroyed. - Fix invalid Atom feeds when using emoji, horizontal rules, or images (Christian Walther) + - Backup of repositories with tar instead of git bundle (only now are git-annex files included in the backup) + - Add canceled status for CI v 7.8.4 - Fix issue_tracker_id substitution in custom issue trackers @@ -31,7 +31,7 @@ gem 'omniauth-shibboleth' gem 'omniauth-kerberos' gem 'omniauth-gitlab' gem 'omniauth-bitbucket' -gem 'doorkeeper', '2.1.0' +gem 'doorkeeper', '2.1.3' gem "rack-oauth2", "~> 1.0.5" # Browser detection @@ -48,7 +48,7 @@ gem 'gitlab-grack', '~> 2.0.0.rc2', require: 'grack' gem 'gitlab_omniauth-ldap', '1.2.1', require: "omniauth-ldap" # Git Wiki -gem 'gollum-lib', '~> 4.0.0' +gem 'gollum-lib', '~> 4.0.2' # Language detection gem "gitlab-linguist", "~> 3.0.1", require: "linguist" @@ -121,6 +121,7 @@ gem "acts-as-taggable-on" gem 'slim' gem 'sinatra', require: nil gem 'sidekiq', '~> 3.3' +gem 'sidetiq', '0.6.3' # HTTP requests gem "httparty" @@ -180,9 +181,6 @@ gem 'mousetrap-rails' # Detect and convert string character encoding gem 'charlock_holmes' -# Shutting down requests that take too long -gem "slowpoke" - gem "sass-rails", '~> 4.0.2' gem "coffee-rails" gem "uglifier" diff --git a/Gemfile.lock b/Gemfile.lock index a454461ec26..4f1cab43dd5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -136,8 +136,8 @@ GEM diff-lcs (1.2.5) diffy (3.0.3) docile (1.1.5) - doorkeeper (2.1.0) - railties (>= 3.1) + doorkeeper (2.1.3) + railties (>= 3.2) dotenv (0.9.0) dropzonejs-rails (0.4.14) rails (> 3.1) @@ -147,7 +147,6 @@ GEM enumerize (0.7.0) activesupport (>= 3.2) equalizer (0.0.8) - errbase (0.0.2) erubis (2.7.0) escape_utils (0.2.4) eventmachine (1.0.4) @@ -224,11 +223,11 @@ GEM omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.3) - gollum-grit_adapter (0.1.0) - gitlab-grit (~> 2.7.1) - gollum-lib (4.0.0) + gollum-grit_adapter (0.1.3) + gitlab-grit (~> 2.7, >= 2.7.1) + gollum-lib (4.0.2) github-markup (~> 1.3.1) - gollum-grit_adapter (~> 0.1.0) + gollum-grit_adapter (~> 0.1, >= 0.1.1) nokogiri (~> 1.6.4) rouge (~> 1.7.4) sanitize (~> 2.1.0) @@ -291,6 +290,7 @@ GEM httpauth (0.2.1) httpclient (2.5.3.3) i18n (0.7.0) + ice_cube (0.11.1) ice_nine (0.10.0) jasmine (2.0.2) jasmine-core (~> 2.0.0) @@ -429,7 +429,6 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rack-timeout (0.2.0) rails (4.1.9) actionmailer (= 4.1.9) actionpack (= 4.1.9) @@ -482,9 +481,7 @@ GEM rest-client (1.6.7) mime-types (>= 1.16) rinku (1.7.3) - robustly (0.0.3) - errbase - rouge (1.7.4) + rouge (1.7.7) rspec (2.99.0) rspec-core (~> 2.99.0) rspec-expectations (~> 2.99.0) @@ -519,7 +516,7 @@ GEM rubyntlm (0.5.0) rubypants (0.2.0) rugged (0.21.4) - rugments (1.0.0.beta4) + rugments (1.0.0.beta5) safe_yaml (0.9.7) sanitize (2.1.0) nokogiri (>= 1.4.4) @@ -550,6 +547,10 @@ GEM json redis (>= 3.0.6) redis-namespace (>= 1.3.1) + sidetiq (0.6.3) + celluloid (>= 0.14.1) + ice_cube (= 0.11.1) + sidekiq (>= 3.0.0) simple_oauth (0.1.9) simplecov (0.9.0) docile (~> 1.1.0) @@ -566,9 +567,6 @@ GEM temple (~> 0.6.6) tilt (>= 1.3.3, < 2.1) slop (3.6.0) - slowpoke (0.0.5) - rack-timeout (>= 0.1.0) - robustly spinach (0.8.7) colorize (= 0.5.8) gherkin-ruby (>= 0.3.1) @@ -690,7 +688,7 @@ DEPENDENCIES devise (= 3.2.4) devise-async (= 0.9.0) diffy (~> 3.0.3) - doorkeeper (= 2.1.0) + doorkeeper (= 2.1.3) dropzonejs-rails email_spec enumerize @@ -708,7 +706,7 @@ DEPENDENCIES gitlab_git (~> 7.1.2) gitlab_meta (= 7.0) gitlab_omniauth-ldap (= 1.2.1) - gollum-lib (~> 4.0.0) + gollum-lib (~> 4.0.2) gon (~> 5.0.0) grape (~> 0.6.1) grape-entity (~> 0.4.2) @@ -771,12 +769,12 @@ DEPENDENCIES settingslogic shoulda-matchers (~> 2.7.0) sidekiq (~> 3.3) + sidetiq (= 0.6.3) simplecov sinatra six slack-notifier (~> 1.0.0) slim - slowpoke spinach-rails spring (~> 1.3.1) spring-commands-rspec (= 1.0.4) diff --git a/app/assets/images/authbuttons/bitbucket_32.png b/app/assets/images/authbuttons/bitbucket_32.png Binary files differdeleted file mode 100644 index 27702eb973d..00000000000 --- a/app/assets/images/authbuttons/bitbucket_32.png +++ /dev/null diff --git a/app/assets/images/authbuttons/github_32.png b/app/assets/images/authbuttons/github_32.png Binary files differdeleted file mode 100644 index 0445b567bbc..00000000000 --- a/app/assets/images/authbuttons/github_32.png +++ /dev/null diff --git a/app/assets/images/authbuttons/gitlab_32.png b/app/assets/images/authbuttons/gitlab_32.png Binary files differdeleted file mode 100644 index f3b78cb6efb..00000000000 --- a/app/assets/images/authbuttons/gitlab_32.png +++ /dev/null diff --git a/app/assets/images/authbuttons/gitlab_64.png b/app/assets/images/authbuttons/gitlab_64.png Binary files differindex ff2945fe89e..31281a19444 100644 --- a/app/assets/images/authbuttons/gitlab_64.png +++ b/app/assets/images/authbuttons/gitlab_64.png diff --git a/app/assets/images/authbuttons/google_32.png b/app/assets/images/authbuttons/google_32.png Binary files differdeleted file mode 100644 index b03c3ec5207..00000000000 --- a/app/assets/images/authbuttons/google_32.png +++ /dev/null diff --git a/app/assets/images/authbuttons/twitter_32.png b/app/assets/images/authbuttons/twitter_32.png Binary files differdeleted file mode 100644 index a3d4964f40f..00000000000 --- a/app/assets/images/authbuttons/twitter_32.png +++ /dev/null diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index f2753170478..bf71c144eaf 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -19,6 +19,6 @@ class @Issue $('.issue-details').waitForImages -> $('.issuable-affix').affix offset: top: -> - @top = $('.issue-details').outerHeight(true) + 25 + @top = ($('.issuable-affix').offset().top - 70) bottom: -> @bottom = $('.footer').outerHeight(true) diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 1fee9dc1892..09c202e42a5 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -23,7 +23,7 @@ class @MergeRequest $('.merge-request-details').waitForImages -> $('.issuable-affix').affix offset: top: -> - @top = $('.merge-request-details').outerHeight(true) + 91 + @top = ($('.issuable-affix').offset().top - 70) bottom: -> @bottom = $('.footer').outerHeight(true) @@ -110,7 +110,7 @@ class @MergeRequest showCiState: (state) -> $('.ci_widget').hide() - allowed_states = ["failed", "running", "pending", "success"] + allowed_states = ["failed", "canceled", "running", "pending", "success"] if state in allowed_states $('.ci_widget.ci-' + state).show() else diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 90e6fd6d154..c366c98cf54 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -425,7 +425,7 @@ class @Notes @removeDiscussionNoteForm(form) updateVotes: -> - (new NotesVotes).updateVotes() + true ### Called after an attachment file has been selected. diff --git a/app/assets/javascripts/notes_votes.js.coffee b/app/assets/javascripts/notes_votes.js.coffee deleted file mode 100644 index 65c149b7886..00000000000 --- a/app/assets/javascripts/notes_votes.js.coffee +++ /dev/null @@ -1,20 +0,0 @@ -class @NotesVotes - updateVotes: -> - votes = $("#votes .votes") - notes = $("#notes-list .note .vote") - - # only update if there is a vote display - if votes.size() - upvotes = notes.filter(".upvote").size() - downvotes = notes.filter(".downvote").size() - votesCount = upvotes + downvotes - upvotesPercent = (if votesCount then (100.0 / votesCount * upvotes) else 0) - downvotesPercent = (if votesCount then (100.0 - upvotesPercent) else 0) - - # change vote bar lengths - votes.find(".bar-success").css "width", upvotesPercent + "%" - votes.find(".bar-danger").css "width", downvotesPercent + "%" - - # replace vote numbers - votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes) - votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes) diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 876eea72e8a..db393e08819 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -355,3 +355,22 @@ table { bottom: 20px !important; left: 20px !important; } + +.header-with-avatar { + h3 { + margin: 0; + font-weight: bold; + } + + .username { + font-size: 18px; + color: #666; + margin-top: 8px; + } + + .description { + font-size: 16px; + color: #666; + margin-top: 8px; + } +} diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss index 31fe5a03f37..266041403e0 100644 --- a/app/assets/stylesheets/generic/forms.scss +++ b/app/assets/stylesheets/generic/forms.scss @@ -15,10 +15,6 @@ input[type='text'].danger { text-shadow: 0 1px 1px #fff } -fieldset legend { - font-size: 16px; -} - .datetime-controls { select { width: 100px; diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss index e408211fc7d..af9c83e5dc8 100644 --- a/app/assets/stylesheets/pages/dashboard.scss +++ b/app/assets/stylesheets/pages/dashboard.scss @@ -29,7 +29,7 @@ line-height: 24px; .str-truncated { - max-width: 75%; + max-width: 72%; } a { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 83f65913ee6..7b7bb88bc20 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -11,8 +11,10 @@ z-index: 10; > span { - @include str-truncated(65%); font-family: $monospace_font; + word-break: break-all; + margin-right: 200px; + display: block; } .diff-btn-group { diff --git a/app/assets/stylesheets/pages/header.scss b/app/assets/stylesheets/pages/header.scss index 26b4d04106e..dde19b801f8 100644 --- a/app/assets/stylesheets/pages/header.scss +++ b/app/assets/stylesheets/pages/header.scss @@ -31,7 +31,7 @@ header { .navbar-toggle { color: $style_color; - margin: 0 -15px 0 0; + margin: 0; padding: 10px; border-radius: 0; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index d8d12338859..a640a4e2051 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -25,7 +25,7 @@ } .issuable-context-title { - font-size: 15px; + font-size: 14px; line-height: 1.4; margin-bottom: 5px; @@ -39,3 +39,9 @@ margin-right: 4px; } } + +.issuable-affix .context { + font-size: 13px; + + .btn { font-size: 13px; } +} diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index d366300511e..83b866c3a64 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -113,3 +113,12 @@ } } } + +.oauth-image-link { + margin-right: 10px; + + img { + width: 32px; + height: 32px; + } +} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 394b59b7e4b..d8fe339b7b3 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -155,6 +155,12 @@ background-color: #FAF1F1; } + &.ci-canceled { + color: $gl-warning; + border-color: $gl-danger; + background-color: #FAF5F1; + } + &.ci-error { color: $gl-danger; border-color: $gl-danger; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 81afe05162f..65655d4bfa3 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -1,31 +1,7 @@ .account-page { fieldset { margin-bottom: 15px; - border-bottom: 1px dashed #ddd; padding-bottom: 15px; - - &:last-child { - border: none; - } - - legend { - border: none; - margin-bottom: 10px; - } - } -} - -.oauth_select_holder { - img { - padding: 2px; - margin-right: 10px; - } - .active { - img { - border: 1px solid #4BD; - background: $hover; - @include border-radius(5px); - } } } @@ -41,11 +17,6 @@ } } -.user-show-username { - font-weight: 200; - color: #666; -} - /* * Appearance settings * @@ -106,3 +77,19 @@ } } } + +.oauth-buttons { + .btn-group { + margin-right: 10px; + } + + .btn { + line-height: 36px; + height: 56px; + + img { + width: 32px; + height: 32px; + } + } +} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 74755951670..6d55a5fa66e 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -49,16 +49,20 @@ @extend .clearfix; margin-bottom: 15px; + .project-home-desc, + .star-fork-buttons { + font-size: 16px; + line-height: 1.3; + } + .project-home-desc { float: left; color: #666; - font-size: 16px; } .star-fork-buttons { float: right; min-width: 200px; - font-size: 14px; font-weight: bold; .star-buttons, .fork-buttons { diff --git a/app/assets/stylesheets/pages/votes.scss b/app/assets/stylesheets/pages/votes.scss index ba0a519dca6..dc9a7d71e8b 100644 --- a/app/assets/stylesheets/pages/votes.scss +++ b/app/assets/stylesheets/pages/votes.scss @@ -1,39 +1,4 @@ -.votes { - font-size: 13px; - line-height: 15px; - .progress { - height: 4px; - margin: 0; - .bar { - float: left; - height: 100%; - } - .bar-success { - @include linear-gradient(#62C462, #51A351); - background-color: #468847; - } - .bar-danger { - @include linear-gradient(#EE5F5B, #BD362F); - background-color: #B94A48; - } - } - .upvotes { - display: inline-block; - color: #468847; - } - .downvotes { - display: inline-block; - color: #B94A48; - } -} -.votes-block { - margin: 6px; - .downvotes { - float: right; - } -} .votes-inline { display: inline-block; margin: 0 8px; } - diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index 42dbf4d6ef3..1be0551ad3b 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -11,3 +11,7 @@ header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {di .wiki h1 {font-size: 30px;} .wiki h2 {font-size: 22px;} .wiki h3 {font-size: 18px; font-weight: bold; } + +.sidebar-wrapper { display: none; } +.nav { display: none; } +.btn { display: none; } diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb index fe121691a10..9bd34fe2261 100644 --- a/app/controllers/profiles/accounts_controller.rb +++ b/app/controllers/profiles/accounts_controller.rb @@ -4,4 +4,10 @@ class Profiles::AccountsController < ApplicationController def show @user = current_user end + + def unlink + provider = params[:provider] + current_user.identities.find_by(provider: provider).destroy + redirect_to profile_account_path + end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 1b9a86ee42c..9252e85e8cc 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -25,7 +25,8 @@ class ProfilesController < ApplicationController if @user.update_attributes(user_params) flash[:notice] = "Profile was successfully updated" else - flash[:alert] = "Failed to update profile" + messages = @user.errors.full_messages.uniq.join('. ') + flash[:alert] = "Failed to update profile. #{messages}" end respond_to do |format| @@ -68,7 +69,7 @@ class ProfilesController < ApplicationController params.require(:user).permit( :email, :password, :password_confirmation, :bio, :name, :username, :skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, - :avatar, :hide_no_ssh_key, :hide_no_password + :avatar, :hide_no_ssh_key, :hide_no_password, :location ) end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index c63a9b0cd44..e9b7d7e0083 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -160,10 +160,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController def ci_status ci_service = @merge_request.source_project.ci_service - status = ci_service.commit_status(merge_request.last_commit.sha) + status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch) if ci_service.respond_to?(:commit_coverage) - coverage = ci_service.commit_coverage(merge_request.last_commit.sha) + coverage = ci_service.commit_coverage(merge_request.last_commit.sha, merge_request.source_branch) end response = { diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 55926a1ed22..a3284c82d3f 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -2,34 +2,34 @@ class SearchController < ApplicationController include SearchHelper def show + return if params[:search].nil? || params[:search].blank? @project = Project.find_by(id: params[:project_id]) if params[:project_id].present? @group = Group.find_by(id: params[:group_id]) if params[:group_id].present? @scope = params[:scope] @show_snippets = params[:snippets].eql? 'true' - @search_results = if @project - return access_denied! unless can?(current_user, :download_code, @project) - - unless %w(blobs notes issues merge_requests wiki_blobs). - include?(@scope) - @scope = 'blobs' - end - - Search::ProjectService.new(@project, current_user, params).execute - elsif @show_snippets - unless %w(snippet_blobs snippet_titles).include?(@scope) - @scope = 'snippet_blobs' - end - - Search::SnippetService.new(current_user, params).execute - else - unless %w(projects issues merge_requests).include?(@scope) - @scope = 'projects' - end - - Search::GlobalService.new(current_user, params).execute - end - + @search_results = + if @project + return access_denied! unless can?(current_user, :download_code, @project) + + unless %w(blobs notes issues merge_requests wiki_blobs). + include?(@scope) + @scope = 'blobs' + end + + Search::ProjectService.new(@project, current_user, params).execute + elsif @show_snippets + unless %w(snippet_blobs snippet_titles).include?(@scope) + @scope = 'snippet_blobs' + end + + Search::SnippetService.new(current_user, params).execute + else + unless %w(projects issues merge_requests).include?(@scope) + @scope = 'projects' + end + Search::GlobalService.new(current_user, params).execute + end @objects = @search_results.objects(@scope, params[:page]) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8ed6d59c20d..38b5fc4a011 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -174,16 +174,10 @@ module ApplicationHelper Digest::SHA1.hexdigest string end - def authbutton(provider, size = 64) - file_name = "#{provider.to_s.split('_').first}_#{size}.png" - image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}") - end - def simple_sanitize(str) sanitize(str, tags: %w(a span)) end - def body_data_page path = controller.controller_path.split('/') namespace = path.first if path.second diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index b005cb8e417..3386fac8657 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -47,6 +47,5 @@ module GitlabRoutingHelper def project_snippet_url(entity, *args) namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) - end end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 3b1589da57f..51b60770e0b 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -35,7 +35,7 @@ module MergeRequestsHelper end def ci_build_details_path(merge_request) - merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha) + merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch) end def merge_path_description(merge_request, separator) diff --git a/app/helpers/oauth_helper.rb b/app/helpers/oauth_helper.rb index 1a0ad17b607..997b91de077 100644 --- a/app/helpers/oauth_helper.rb +++ b/app/helpers/oauth_helper.rb @@ -20,6 +20,15 @@ module OauthHelper def additional_providers enabled_oauth_providers.reject{|provider| provider.to_s.starts_with?('ldap')} end - + + def oauth_image_tag(provider, size = 64) + file_name = "#{provider.to_s.split('_').first}_#{size}.png" + image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}") + end + + def oauth_active?(provider) + current_user.identities.exists?(provider: provider.to_s) + end + extend self end diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb index 9e37e44732a..780c7cd5133 100644 --- a/app/helpers/profile_helper.rb +++ b/app/helpers/profile_helper.rb @@ -1,10 +1,4 @@ module ProfileHelper - def oauth_active_class(provider) - if current_user.identities.exists?(provider: provider.to_s) - 'active' - end - end - def show_profile_username_tab? current_user.can_change_username? end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 4cbdc612297..798306f6dcc 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -95,11 +95,11 @@ class MergeRequest < ActiveRecord::Base end event :mark_as_mergeable do - transition unchecked: :can_be_merged + transition [:unchecked, :cannot_be_merged] => :can_be_merged end event :mark_as_unmergeable do - transition unchecked: :cannot_be_merged + transition [:unchecked, :can_be_merged] => :cannot_be_merged end state :unchecked diff --git a/app/models/note.rb b/app/models/note.rb index 649e9b4e852..27b583a869a 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -59,7 +59,7 @@ class Note < ActiveRecord::Base class << self def create_status_change_note(noteable, project, author, status, source) - body = "_Status changed to #{status}#{' by ' + source.gfm_reference if source}_" + body = "Status changed to #{status}#{' by ' + source.gfm_reference if source}" create( noteable: noteable, @@ -95,9 +95,9 @@ class Note < ActiveRecord::Base def create_milestone_change_note(noteable, project, author, milestone) body = if milestone.nil? - '_Milestone removed_' + 'Milestone removed' else - "_Milestone changed to #{milestone.title}_" + "Milestone changed to #{milestone.title}" end create( @@ -110,7 +110,7 @@ class Note < ActiveRecord::Base end def create_assignee_change_note(noteable, project, author, assignee) - body = assignee.nil? ? '_Assignee removed_' : "_Reassigned to @#{assignee.username}_" + body = assignee.nil? ? 'Assignee removed' : "Reassigned to @#{assignee.username}" create({ noteable: noteable, @@ -140,7 +140,7 @@ class Note < ActiveRecord::Base end message << ' ' << 'label'.pluralize(labels_count) - body = "_#{message.capitalize}_" + body = "#{message.capitalize}" create( noteable: noteable, @@ -170,14 +170,14 @@ class Note < ActiveRecord::Base commits_text = ActionController::Base.helpers.pluralize(existing_commits.length, 'commit') - branch = + branch = if merge_request.for_fork? "#{merge_request.target_project_namespace}:#{merge_request.target_branch}" else merge_request.target_branch end - message = "* #{commit_ids} - _#{commits_text} from branch `#{branch}`_" + message = "* #{commit_ids} - #{commits_text} from branch `#{branch}`" body << message body << "\n" end @@ -240,7 +240,7 @@ class Note < ActiveRecord::Base where(noteable_id: noteable.id) end - notes.where('note like ?', cross_reference_note_content(gfm_reference)). + notes.where('note like ?', cross_reference_note_pattern(gfm_reference)). system.any? end @@ -249,13 +249,18 @@ class Note < ActiveRecord::Base end def cross_reference_note_prefix - '_mentioned in ' + 'mentioned in ' end private def cross_reference_note_content(gfm_reference) - cross_reference_note_prefix + "#{gfm_reference}_" + cross_reference_note_prefix + "#{gfm_reference}" + end + + def cross_reference_note_pattern(gfm_reference) + # Older cross reference notes contained underscores for emphasis + "%" + cross_reference_note_content(gfm_reference) + "%" end # Prepend the mentioner's namespaced project path to the GFM reference for diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 0100f1e4a10..f968afe9fa8 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -93,7 +93,7 @@ class BambooService < CiService end end - def build_page(sha) + def build_page(sha, ref) build_info(sha) if @response.nil? || !@response.code if @response.code != 200 || @response['results']['results']['size'] == '0' @@ -106,7 +106,7 @@ class BambooService < CiService end end - def commit_status(sha) + def commit_status(sha, ref) build_info(sha) if @response.nil? || !@response.code return :error unless @response.code == 200 || @response.code == 404 diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildbox_service.rb index 270863c1576..fef1c9b7349 100644 --- a/app/models/project_services/buildbox_service.rb +++ b/app/models/project_services/buildbox_service.rb @@ -48,7 +48,7 @@ class BuildboxService < CiService service_hook.execute(data) end - def commit_status(sha) + def commit_status(sha, ref) response = HTTParty.get(commit_status_path(sha), verify: false) if response.code == 200 && response['status'] @@ -62,7 +62,7 @@ class BuildboxService < CiService "#{buildbox_endpoint('gitlab')}/status/#{status_token}.json?commit=#{sha}" end - def build_page(sha) + def build_page(sha, ref) "#{project_url}/builds?commit=#{sha}" end diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index c6f6b4952c9..1a36e439245 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -34,7 +34,7 @@ class CiService < Service # Ex. # http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c # - def build_page(sha) + def build_page(sha, ref) # implement inside child end @@ -51,7 +51,7 @@ class CiService < Service # # => 'running' # # - def commit_status(sha) + def commit_status(sha, ref) # implement inside child end end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index d81623625c9..edaeeffc228 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -40,17 +40,17 @@ class GitlabCiService < CiService service_hook.execute(data) end - def commit_status_path(sha) - project_url + "/commits/#{sha}/status.json?token=#{token}" + def commit_status_path(sha, ref) + project_url + "/refs/#{ref}/commits/#{sha}/status.json?token=#{token}" end - def get_ci_build(sha) + def get_ci_build(sha, ref) @ci_builds ||= {} - @ci_builds[sha] ||= HTTParty.get(commit_status_path(sha), verify: false) + @ci_builds[sha] ||= HTTParty.get(commit_status_path(sha, ref), verify: false) end - def commit_status(sha) - response = get_ci_build(sha) + def commit_status(sha, ref) + response = get_ci_build(sha, ref) if response.code == 200 and response["status"] response["status"] @@ -59,16 +59,16 @@ class GitlabCiService < CiService end end - def commit_coverage(sha) - response = get_ci_build(sha) + def commit_coverage(sha, ref) + response = get_ci_build(sha, ref) if response.code == 200 and response["coverage"] response["coverage"] end end - def build_page(sha) - project_url + "/commits/#{sha}" + def build_page(sha, ref) + project_url + "/refs/#{ref}/commits/#{sha}" end def builds_path diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 7403e19da9a..c26bc551352 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -88,7 +88,7 @@ class TeamcityService < CiService @response = HTTParty.get("#{url}", verify: false, basic_auth: auth) end - def build_page(sha) + def build_page(sha, ref) build_info(sha) if @response.nil? || !@response.code if @response.code != 200 @@ -103,7 +103,7 @@ class TeamcityService < CiService end end - def commit_status(sha) + def commit_status(sha, ref) build_info(sha) if @response.nil? || !@response.code return :error unless @response.code == 200 || @response.code == 404 diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 55438bee245..772c868d9cd 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -104,7 +104,7 @@ class ProjectWiki def page_title_and_dir(title) title_array = title.split("/") title = title_array.pop - [title.gsub(/\.[^.]*$/, ""), title_array.join("/")] + [title, title_array.join("/")] end def search_files(query) diff --git a/app/models/repository.rb b/app/models/repository.rb index c6eaa485b8a..082ad7a0c6a 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -62,24 +62,28 @@ class Repository def add_branch(branch_name, ref) cache.expire(:branch_names) + @branches = nil gitlab_shell.add_branch(path_with_namespace, branch_name, ref) end def add_tag(tag_name, ref, message = nil) cache.expire(:tag_names) + @tags = nil gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) end def rm_branch(branch_name) cache.expire(:branch_names) + @branches = nil gitlab_shell.rm_branch(path_with_namespace, branch_name) end def rm_tag(tag_name) cache.expire(:tag_names) + @tags = nil gitlab_shell.rm_tag(path_with_namespace, tag_name) end @@ -180,8 +184,17 @@ class Repository end end + def lookup_cache + @lookup_cache ||= {} + end + def method_missing(m, *args, &block) - raw_repository.send(m, *args, &block) + if m == :lookup && !block_given? + lookup_cache[m] ||= {} + lookup_cache[m][args.join(":")] ||= raw_repository.send(m, *args, &block) + else + raw_repository.send(m, *args, &block) + end end def respond_to?(method) @@ -235,12 +248,20 @@ class Repository end def head_commit - commit(self.root_ref) + @head_commit ||= commit(self.root_ref) + end + + def head_tree + @head_tree ||= Tree.new(self, head_commit.sha, nil) end def tree(sha = :head, path = nil) if sha == :head - sha = head_commit.sha + if path.nil? + return head_tree + else + sha = head_commit.sha + end end Tree.new(self, sha, path) @@ -368,6 +389,18 @@ class Repository end end + def branches + @branches ||= raw_repository.branches + end + + def tags + @tags ||= raw_repository.tags + end + + def root_ref + @root_ref ||= raw_repository.root_ref + end + private def cache diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 32981a0e664..e9413c34bae 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -179,7 +179,8 @@ class WikiPage if valid? && project_wiki.send(method, *args) page_details = if method == :update_page - @page.path + # Use url_path instead of path to omit format extension + @page.url_path else title end diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index af4b537cb93..4115d689925 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -40,7 +40,8 @@ class CreateTagService < BaseService end def create_push_data(project, user, tag) + commits = [project.repository.commit(tag.target)].compact Gitlab::PushDataBuilder. - build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", []) + build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", commits, tag.message) end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 4885e1b2fc5..1f0b29dff5e 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -23,42 +23,40 @@ class GitPushService project.repository.expire_cache project.update_repository_size - if push_to_branch?(ref) - if push_remove_branch?(ref, newrev) - @push_commits = [] - elsif push_to_new_branch?(ref, oldrev) - # Re-find the pushed commits. - if is_default_branch?(ref) - # Initial push to the default branch. Take the full history of that branch as "newly pushed". - @push_commits = project.repository.commits(newrev) - - # Set protection on the default branch if configured - if (current_application_settings.default_branch_protection != PROTECTION_NONE) - developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false - project.protected_branches.create({ name: project.default_branch, developers_can_push: developers_can_push }) - end - else - # Use the pushed commits that aren't reachable by the default branch - # as a heuristic. This may include more commits than are actually pushed, but - # that shouldn't matter because we check for existing cross-references later. - @push_commits = project.repository.commits_between(project.default_branch, newrev) - - # don't process commits for the initial push to the default branch - process_commit_messages(ref) + if push_remove_branch?(ref, newrev) + @push_commits = [] + elsif push_to_new_branch?(ref, oldrev) + # Re-find the pushed commits. + if is_default_branch?(ref) + # Initial push to the default branch. Take the full history of that branch as "newly pushed". + @push_commits = project.repository.commits(newrev) + + # Set protection on the default branch if configured + if (current_application_settings.default_branch_protection != PROTECTION_NONE) + developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false + project.protected_branches.create({ name: project.default_branch, developers_can_push: developers_can_push }) end - elsif push_to_existing_branch?(ref, oldrev) - # Collect data for this git push - @push_commits = project.repository.commits_between(oldrev, newrev) - project.update_merge_requests(oldrev, newrev, ref, @user) + else + # Use the pushed commits that aren't reachable by the default branch + # as a heuristic. This may include more commits than are actually pushed, but + # that shouldn't matter because we check for existing cross-references later. + @push_commits = project.repository.commits_between(project.default_branch, newrev) + + # don't process commits for the initial push to the default branch process_commit_messages(ref) end + elsif push_to_existing_branch?(ref, oldrev) + # Collect data for this git push + @push_commits = project.repository.commits_between(oldrev, newrev) + project.update_merge_requests(oldrev, newrev, ref, @user) + process_commit_messages(ref) + end - @push_data = build_push_data(oldrev, newrev, ref) + @push_data = build_push_data(oldrev, newrev, ref) - EventCreateService.new.push(project, user, @push_data) - project.execute_hooks(@push_data.dup, :push_hooks) - project.execute_services(@push_data.dup, :push_hooks) - end + EventCreateService.new.push(project, user, @push_data) + project.execute_hooks(@push_data.dup, :push_hooks) + project.execute_services(@push_data.dup, :push_hooks) end protected @@ -109,7 +107,7 @@ class GitPushService def push_to_existing_branch?(ref, oldrev) # Return if this is not a push to a branch (e.g. new commits) - Gitlab::Git.branch_ref?(ref) && oldrev != Gitlab::Git::BLANK_SHA + Gitlab::Git.branch_ref?(ref) && !Gitlab::Git.blank_ref?(oldrev) end def push_to_new_branch?(ref, oldrev) diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 0d8e6e85e47..bf203bbd692 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -3,7 +3,7 @@ class GitTagPushService def execute(project, user, oldrev, newrev, ref) @project, @user = project, user - + @push_data = build_push_data(oldrev, newrev, ref) EventCreateService.new.push(project, user, @push_data) @@ -18,6 +18,20 @@ class GitTagPushService private def build_push_data(oldrev, newrev, ref) - Gitlab::PushDataBuilder.build(project, user, oldrev, newrev, ref, []) + commits = [] + message = nil + + if !Gitlab::Git.blank_ref?(newrev) + tag_name = Gitlab::Git.ref_name(ref) + tag = project.repository.find_tag(tag_name) + if tag && tag.target == newrev + commit = project.repository.commit(tag.target) + commits = [commit].compact + message = tag.message + end + end + + Gitlab::PushDataBuilder. + build(project, user, oldrev, newrev, ref, commits, message) end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 848ed77ebf8..cc5853144c5 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -120,7 +120,7 @@ class NotificationService return true unless note.noteable_type.present? # ignore gitlab service messages - return true if note.note.start_with?('_Status changed to closed_') + return true if note.note.start_with?('Status changed to closed') return true if note.cross_reference? && note.system == true opts = { noteable_type: note.noteable_type, project_id: note.project_id } diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index 4cd1c303b22..b647b906b71 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -5,6 +5,6 @@ - providers.each do |provider| %span.light - if default_providers.include?(provider) - = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) + = link_to oauth_image_tag(provider), omniauth_authorize_path(resource_name, provider), class: 'oauth-image-link' - else = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 25efe973d4f..8df9366ecbe 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,12 +1,13 @@ .dashboard - %div + .header-with-avatar.clearfix = image_tag group_icon(@group), class: "avatar group-avatar s90" - .clearfix - %h2 - = @group.name - - if @group.description.present? - %p - = escaped_autolink(@group.description) + %h3 + = @group.name + .username + @#{@group.path} + - if @group.description.present? + .description + = escaped_autolink(@group.description) %hr .row %section.activities.col-md-8 diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 6bafcb56551..5bffb4acc1d 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -1,11 +1,6 @@ -%h3.page-title - Account Settings -%p.light - You can change your username and private token here. - - if current_user.ldap_user? +- if current_user.ldap_user? + .alert.alert-info Some options are unavailable for LDAP accounts -%hr - .account-page %fieldset.update-token @@ -33,12 +28,16 @@ - if show_profile_social_tab? %fieldset - %legend Social Accounts - .oauth_select_holder.append-bottom-10 + %legend Connected Accounts + .oauth-buttons.append-bottom-10 %p Click on icon to activate signin with one of the following services - enabled_social_providers.each do |provider| - %span{class: oauth_active_class(provider) } - = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) + .btn-group + = link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider), + class: "btn btn-lg #{'active' if oauth_active?(provider)}" + - if oauth_active?(provider) + = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do + %i.fa.fa-close - if show_profile_username_tab? %fieldset.update-username diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 409b6b5a193..5a501e43149 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -54,6 +54,9 @@ = f.label :website_url, 'Website', class: "control-label" .col-sm-10= f.text_field :website_url, class: "form-control" .form-group + = f.label :location, 'Location', class: "control-label" + .col-sm-10= f.text_field :location, class: "form-control" + .form-group = f.label :bio, class: "control-label" .col-sm-10 = f.text_area :bio, rows: 4, class: "form-control", maxlength: 250 diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml index fd8320adb8d..35f7e7bb34b 100644 --- a/app/views/projects/_commit_button.html.haml +++ b/app/views/projects/_commit_button.html.haml @@ -2,8 +2,5 @@ .commit-button-annotation = button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create' - .message - to branch - %strong= ref = link_to 'Cancel', cancel_path, class: 'btn btn-cancel', data: {confirm: leave_edit_message} diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index fc3e35640dc..0d3028d50b4 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -6,11 +6,12 @@ = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue" .row %section.col-md-9 + .votes-holder.pull-right + #votes= render 'votes/votes_block', votable: @issue .participants %span= pluralize(@issue.participants.count, 'participant') - @issue.participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) - .voting_notes#notes= render "projects/notes/notes_with_form" %aside.col-md-3 .issuable-affix @@ -20,15 +21,10 @@ %hr .context = render partial: 'issue_context', locals: { issue: @issue } - %hr - .clearfix - .votes-holder - %h6 Votes - #votes= render 'votes/votes_block', votable: @issue - if @issue.labels.any? - %hr - %h6 Labels + .issuable-context-title + %label Labels .issue-show-labels - @issue.labels.each do |label| = link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 3b50ce01351..7b06fe72882 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -17,7 +17,7 @@ = issue.notes.count .issue-info - %span.light= "##{issue.iid}" + = link_to "##{issue.iid}", issue_path(issue), class: "light" - if issue.assignee assigned to #{link_to_member(@project, issue.assignee)} - if issue.votes_count > 0 diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index d43ce0aa293..91fe0b68371 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -45,5 +45,5 @@ :coffeescript $ -> new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}") - -
\ No newline at end of file + + diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 79a093dc775..eb72eaabd8b 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -7,6 +7,8 @@ .row %section.col-md-9 + .votes-holder.pull-right + #votes= render 'votes/votes_block', votable: @merge_request = render "projects/merge_requests/show/participants" = render "projects/notes/notes_with_form" %aside.col-md-3 @@ -17,14 +19,10 @@ %hr .context = render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request } - %hr - .votes-holder - %h6 Votes - #votes= render 'votes/votes_block', votable: @merge_request - if @merge_request.labels.any? - %hr - %h6 Labels + .issuable-context-title + %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 diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 1eba1a96b7b..ecbff722b42 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -22,7 +22,7 @@ %i.fa.fa-comments = merge_request.mr_and_commit_notes.count .merge-request-info - %span.light= "##{merge_request.iid}" + = link_to "##{merge_request.iid}", merge_request_path(merge_request), class: "light" - if merge_request.assignee assigned to #{link_to_member(merge_request.source_project, merge_request.assignee)} - else diff --git a/app/views/projects/merge_requests/show/_mr_ci.html.haml b/app/views/projects/merge_requests/show/_mr_ci.html.haml index 85a7103f3bc..ffa3f7b0e36 100644 --- a/app/views/projects/merge_requests/show/_mr_ci.html.haml +++ b/app/views/projects/merge_requests/show/_mr_ci.html.haml @@ -23,6 +23,12 @@ %i.fa.fa-spinner Checking for CI status for #{@merge_request.last_commit_short_sha} + .ci_widget.ci-canceled{style: "display:none"} + %i.fa.fa-times + %span CI build canceled + for #{@merge_request.last_commit_short_sha}. + = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + .ci_widget.ci-error{style: "display:none"} %i.fa.fa-times %span Cannot connect to the CI server. Please check your settings and try again. diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 7fc612c0c7d..9687c8ad87c 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -21,64 +21,65 @@ = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} %hr - .js-toggle-container + + .project-import.js-toggle-container .form-group - .col-sm-2 + %label.control-label Import project from .col-sm-10 - = link_to "#", class: 'js-toggle-button' do - %i.fa.fa-upload - %span Import existing repository by URL - .js-toggle-content.hide - .form-group.import-url-data - = f.label :import_url, class: 'control-label' do - %span Import existing git repo - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .alert.alert-info.prepend-top-10 - This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. - %br - The import will time out after 4 minutes. For big repositories, use a clone/push combination. - For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"} + - if github_import_enabled? + = link_to status_import_github_path, class: 'btn' do + %i.fa.fa-github + GitHub + - else + = link_to '#', class: 'how_to_import_link light btn' do + %i.fa.fa-github + GitHub + = render 'github_import_modal' - .project-import.form-group - %label.control-label Import projects from - .col-sm-10 - - if github_import_enabled? - = link_to status_import_github_path, class: 'btn' do - %i.fa.fa-github - GitHub - - else - = link_to '#', class: 'how_to_import_link light btn' do - %i.fa.fa-github - GitHub - = render 'github_import_modal' - - - - if bitbucket_import_enabled? - = link_to status_import_bitbucket_path, class: 'btn' do - %i.fa.fa-bitbucket - Bitbucket - - else - = link_to '#', class: 'how_to_import_link light btn' do - %i.fa.fa-bitbucket - Bitbucket - = render 'bitbucket_import_modal' - - - unless request.host == 'gitlab.com' - - if gitlab_import_enabled? - = link_to status_import_gitlab_path, class: 'btn' do - %i.fa.fa-heart - GitLab.com + - if bitbucket_import_enabled? + = link_to status_import_bitbucket_path, class: 'btn' do + %i.fa.fa-bitbucket + Bitbucket - else = link_to '#', class: 'how_to_import_link light btn' do - %i.fa.fa-heart - GitLab.com - = render 'gitlab_import_modal' + %i.fa.fa-bitbucket + Bitbucket + = render 'bitbucket_import_modal' - = link_to new_import_gitorious_path, class: 'btn' do - %i.icon-gitorious.icon-gitorious-small - Gitorious.org + - unless request.host == 'gitlab.com' + - if gitlab_import_enabled? + = link_to status_import_gitlab_path, class: 'btn' do + %i.fa.fa-heart + GitLab.com + - else + = link_to '#', class: 'how_to_import_link light btn' do + %i.fa.fa-heart + GitLab.com + = render 'gitlab_import_modal' + + = link_to new_import_gitorious_path, class: 'btn' do + %i.icon-gitorious.icon-gitorious-small + Gitorious.org + + = link_to "#", class: 'btn js-toggle-button' do + %i.fa.fa-git + %span Any repo by URL + + .js-toggle-content.hide + .form-group.import-url-data + = f.label :import_url, class: 'control-label' do + %span Git repository URL + .col-sm-10 + = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git' + .alert.alert-info.prepend-top-10 + %ul + %li + The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>. + %li + The import will time out after 4 minutes. For big repositories, use a clone/push combination. + %li + To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}. %hr.prepend-botton-10 @@ -111,6 +112,6 @@ $ -> $('.how_to_import_link').bind 'click', (e) -> e.preventDefault() - import_modal = $(this).parent().find(".modal").show() + import_modal = $(this).next(".modal").show() $('.modal-header .close').bind 'click', -> $(".modal").hide() diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index cfa6cda0466..cfa6f558dd6 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -73,7 +73,7 @@ %span.light Created on #{@project.created_at.stamp('Aug 22, 2013')} %p - %span.light Owned by + %span.light Owned by #{@project.group ? "the" : nil} - if @project.group #{link_to @project.group.name, @project.group} group - else diff --git a/app/views/users/_profile.html.haml b/app/views/users/_profile.html.haml index 0a70b738071..bca71444956 100644 --- a/app/views/users/_profile.html.haml +++ b/app/views/users/_profile.html.haml @@ -21,7 +21,7 @@ %li %span.light Website: %strong= link_to user.short_website_url, user.full_website_url - - unless user.bio.blank? + - unless user.location.blank? %li - %span.light Bio: - %span= user.bio + %span.light Location: + %strong= user.location diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 6d6beb58711..fd96020d129 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -2,24 +2,28 @@ = link_to '#aside', class: 'show-aside' do %i.fa.fa-angle-left %section.col-md-8 - %h3.page-title + .header-with-avatar = image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: '' - = @user.name - - if @user == current_user - .pull-right - = link_to profile_path, class: 'btn' do - %i.fa.fa-pencil-square-o - Edit Profile settings - %br - %span.user-show-username #{@user.username} - %br - %small member since #{@user.created_at.stamp("Nov 12, 2031")} + %h3 + = @user.name + - if @user == current_user + .pull-right + = link_to profile_path, class: 'btn btn-sm' do + %i.fa.fa-pencil-square-o + Edit Profile settings + .username + @#{@user.username} + .description + - if @user.bio.present? + = @user.bio + .clearfix - if @groups.any? - %h4 Groups - = render 'groups', groups: @groups - %hr + .prepend-top-20 + %h4 Groups + = render 'groups', groups: @groups + %hr .hidden-xs .user-calendar diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 788d9065a7b..36ea6742064 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,6 +1,10 @@ .votes.votes-block - .progress - .progress-bar.progress-bar-success{style: "width: #{votable.upvotes_in_percent}%;"} - .progress-bar.progress-bar-danger{style: "width: #{votable.downvotes_in_percent}%;"} - .upvotes= "#{votable.upvotes} up" - .downvotes= "#{votable.downvotes} down" + .btn-group + - unless votable.upvotes.zero? + .btn.btn-sm.disabled.cgreen + %i.fa.fa-thumbs-up + = votable.upvotes + - unless votable.downvotes.zero? + .btn.btn-sm.disabled.cred + %i.fa.fa-thumbs-down + = votable.downvotes diff --git a/app/views/votes/_votes_inline.html.haml b/app/views/votes/_votes_inline.html.haml index ee805474830..2cb3ae04e1a 100644 --- a/app/views/votes/_votes_inline.html.haml +++ b/app/views/votes/_votes_inline.html.haml @@ -1,9 +1,9 @@ .votes.votes-inline - unless votable.upvotes.zero? - .upvotes + %span.upvotes.cgreen + #{votable.upvotes} - unless votable.downvotes.zero? \/ - unless votable.downvotes.zero? - .downvotes + %span.downvotes.cred \- #{votable.downvotes} diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb index e1a99d9cad8..8b50f423984 100644 --- a/app/workers/irker_worker.rb +++ b/app/workers/irker_worker.rb @@ -57,9 +57,9 @@ class IrkerWorker end def send_branch_updates(push_data, project, repo_name, committer, branch) - if push_data['before'] == Gitlab::Git::BLANK_SHA + if Gitlab::Git.blank_ref?(push_data['before']) send_new_branch project, repo_name, committer, branch - elsif push_data['after'] == Gitlab::Git::BLANK_SHA + elsif Gitlab::Git.blank_ref?(push_data['after']) send_del_branch repo_name, committer, branch end end @@ -83,7 +83,7 @@ class IrkerWorker return if push_data['total_commits_count'] == 0 # Next message is for number of commit pushed, if any - if push_data['before'] == Gitlab::Git::BLANK_SHA + if Gitlab::Git.blank_ref?(push_data['before']) # Tweak on push_data["before"] in order to have a nice compare URL push_data['before'] = before_on_new_branch push_data, project end diff --git a/config/initializers/public_key.rb b/config/initializers/public_key.rb index 75d74e3625d..e4f09a2d020 100644 --- a/config/initializers/public_key.rb +++ b/config/initializers/public_key.rb @@ -1,2 +1,2 @@ -path = File.expand_path("~/.ssh/id_rsa.pub") +path = File.expand_path("~/.ssh/bitbucket_rsa.pub") Gitlab::BitbucketImport.public_key = File.read(path) if File.exist?(path) diff --git a/config/initializers/timeout.rb b/config/initializers/timeout.rb deleted file mode 100644 index bc88595cf26..00000000000 --- a/config/initializers/timeout.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Slowpoke extends Rack::Timeout to gracefully kill Unicorn workers so they can clean up state. -Slowpoke.timeout = 60 - -# The `Rack::Timeout` middleware kills requests after 60 seconds (as set above). -# We're replacing it with our `Gitlab::Middleware::Timeout` that does the same, -# except ignoring Git-over-HTTP requests, letting those take as long as they need. - -Rails.application.config.middleware.swap(Rack::Timeout, Gitlab::Middleware::Timeout) diff --git a/config/routes.rb b/config/routes.rb index 0950bed3cf1..c30cd768572 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -184,7 +184,11 @@ Gitlab::Application.routes.draw do end scope module: :profiles do - resource :account, only: [:show, :update] + resource :account, only: [:show, :update] do + member do + delete :unlink + end + end resource :notifications, only: [:show, :update] resource :password, only: [:new, :create, :edit, :update] do member do @@ -242,7 +246,7 @@ Gitlab::Application.routes.draw do resources :group_members, only: [:index, :create, :update, :destroy] do delete :leave, on: :collection end - + resource :avatar, only: [:destroy] resources :milestones, only: [:index, :show, :update] end @@ -318,14 +322,6 @@ Gitlab::Application.routes.draw do as: :tree ) end - resource :avatar, only: [:show, :destroy] - - resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do - get :branches, on: :member - end - - resources :commits, only: [:show], constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } - resources :compare, only: [:index, :create] scope do get( @@ -336,8 +332,24 @@ Gitlab::Application.routes.draw do ) end - resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } - resources :graphs, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } do + scope do + get( + '/commits/*id', + to: 'commits#show', + constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }, + as: :commits + ) + end + + resource :avatar, only: [:show, :destroy] + resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do + get :branches, on: :member + end + + resources :compare, only: [:index, :create] + resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } + + resources :graphs, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } do member do get :commits end diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example index 3aee718097f..86a5512e761 100644 --- a/config/unicorn.rb.example +++ b/config/unicorn.rb.example @@ -35,10 +35,22 @@ working_directory "/home/git/gitlab" # available in 0.94.0+ listen "/home/git/gitlab/tmp/sockets/gitlab.socket", :backlog => 1024 listen "127.0.0.1:8080", :tcp_nopush => true -# Kill workers after 1 hour. -# A shorter timeout of 60 seconds is enforced by rack-timeout for web requests. -# Git-over-HTTP only has the below timeout since large pulls/pushes can take a long time. -timeout 60 * 60 +# nuke workers after 30 seconds instead of 60 seconds (the default) +# +# NOTICE: git push over http depends on this value. +# If you want be able to push huge amount of data to git repository over http +# you will have to increase this value too. +# +# Example of output if you try to push 1GB repo to GitLab over http. +# -> git push http://gitlab.... master +# +# error: RPC failed; result=18, HTTP code = 200 +# fatal: The remote end hung up unexpectedly +# fatal: The remote end hung up unexpectedly +# +# For more information see http://stackoverflow.com/a/21682112/752049 +# +timeout 60 # feel free to point this anywhere accessible on the filesystem pid "/home/git/gitlab/tmp/pids/unicorn.pid" diff --git a/db/migrate/20150320234437_add_location_to_user.rb b/db/migrate/20150320234437_add_location_to_user.rb new file mode 100644 index 00000000000..32731d37d75 --- /dev/null +++ b/db/migrate/20150320234437_add_location_to_user.rb @@ -0,0 +1,5 @@ +class AddLocationToUser < ActiveRecord::Migration + def change + add_column :users, :location, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 1be3782dcb3..e1a5b70532a 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: 20150313012111) do +ActiveRecord::Schema.define(version: 20150320234437) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -473,6 +473,7 @@ ActiveRecord::Schema.define(version: 20150313012111) do t.boolean "password_automatically_set", default: false t.string "bitbucket_access_token" t.string "bitbucket_access_token_secret" + t.string "location" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 1f3fd26a241..6a272539e45 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -375,7 +375,7 @@ Parameters: } }, { - "note": "_Status changed to closed_", + "note": "Status changed to closed", "author": { "id": 11, "username": "admin", diff --git a/doc/api/notes.md b/doc/api/notes.md index c22e493562a..ee2f9fa0eac 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -21,7 +21,7 @@ Parameters: [ { "id": 302, - "body": "_Status changed to closed_", + "body": "Status changed to closed", "attachment": null, "author": { "id": 1, diff --git a/doc/install/requirements.md b/doc/install/requirements.md index f42af65796f..7a3216dd2d2 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -76,7 +76,7 @@ Notice: The 25 workers of Sidekiq will show up as separate processes in your pro ## Unicorn Workers -It's possible to increase the amount of unicorn workers and tis will usually help for to reduce the response time of the applications and increase the ability to handle parallel requests. +It's possible to increase the amount of unicorn workers and this will usually help for to reduce the response time of the applications and increase the ability to handle parallel requests. For most instances we recommend using: CPU cores + 1 = unicorn workers. So for a machine with 2 cores, 3 unicorn workers is ideal. @@ -106,4 +106,4 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o - Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/)) - Safari 7+ (known problem: required fields in html5 do not work) - Opera (Latest released version) -- IE 10+ +- IE 10+
\ No newline at end of file diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index cc6389f5aaf..d82e1f8b41b 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -2,7 +2,7 @@ Import projects from Bitbucket and login to your GitLab instance with your Bitbucket account. -To enable the Bitbucket OmniAuth provider you must register your application with Bitbucket. +To enable the Bitbucket OmniAuth provider you must register your application with Bitbucket. Bitbucket will generate an application ID and secret key for you to use. 1. Sign in to Bitbucket. @@ -19,8 +19,8 @@ Bitbucket will generate an application ID and secret key for you to use. - URL: The URL to your GitLab installation. 'https://gitlab.company.com' 1. Select "Save". -1. You should now see a Key and Secret in the list of OAuth customers. - Keep this page open as you continue configuration. +1. You should now see a Key and Secret in the list of OAuth customers. + Keep this page open as you continue configuration. 1. On your GitLab server, open the configuration file. @@ -70,13 +70,13 @@ Bitbucket will generate an application ID and secret key for you to use. 1. Restart GitLab for the changes to take effect. -On the sign in page there should now be a Bitbucket icon below the regular sign in form. -Click the icon to begin the authentication process. Bitbucket will ask the user to sign in and authorize the GitLab application. +On the sign in page there should now be a Bitbucket icon below the regular sign in form. +Click the icon to begin the authentication process. Bitbucket will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in. ## Bitbucket project import -To allow projects to be imported directly into GitLab, Bitbucket requires two extra setup steps compared to GitHub and GitLab.com. +To allow projects to be imported directly into GitLab, Bitbucket requires two extra setup steps compared to GitHub and GitLab.com. Bitbucket doesn't allow OAuth applications to clone repositories over HTTPS, and instead requires GitLab to use SSH and identify itself using your GitLab server's SSH key. @@ -95,7 +95,7 @@ To allow GitLab to connect to Bitbucket over SSH, you need to add 'bitbucket.org ```sh The authenticity of host 'bitbucket.org (207.223.240.182)' can't be established. RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40. - Are you sure you want to continue connecting (yes/no)? + Are you sure you want to continue connecting (yes/no)? ``` 1. If the fingerprint matches, type `yes` to continue connecting and have 'bitbucket.org' be added to your known hosts. @@ -104,7 +104,7 @@ To allow GitLab to connect to Bitbucket over SSH, you need to add 'bitbucket.org ### Step 2: Public key -To be able to access repositories on Bitbucket, GitLab will automatically register your public key with Bitbucket as a deploy key for the repositories to be imported. Your public key needs to be at `~/.ssh/id_rsa.pub`, which will expand to `/home/git/.ssh/id_rsa.pub` in most configurations. +To be able to access repositories on Bitbucket, GitLab will automatically register your public key with Bitbucket as a deploy key for the repositories to be imported. Your public key needs to be at `~/.ssh/bitbucket_rsa.pub`, which will expand to `/home/git/.ssh/bitbucket_rsa.pub` in most configurations. If you have that file in place, you're all set and should see the "Import projects from Bitbucket" option enabled. If you don't, do the following: @@ -114,6 +114,7 @@ If you have that file in place, you're all set and should see the "Import projec sudo -u git -H ssh-keygen ``` + When asked `Enter file in which to save the key` specify the correct path, eg. `/home/git/.ssh/bitbucket_rsa`. Make sure to use an **empty passphrase**. 2. Restart GitLab to allow it to find the new public key. diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index 64f28d46451..b66583bb363 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -6,7 +6,7 @@ * [Newlines](#newlines) * [Multiple underscores in words](#multiple-underscores-in-words) -* [URL auto-linking](#url-autolinking) +* [URL auto-linking](#url-auto-linking) * [Code and Syntax Highlighting](#code-and-syntax-highlighting) * [Emoji](#emoji) * [Special GitLab references](#special-gitlab-references) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index ad302492556..e29ee2a7b3d 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -1,5 +1,5 @@ # Universal update guide for patch versions -*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/patch_versions.md) for the most up to date instructions.* +*Make sure you view this [upgrade guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/patch_versions.md) from the `master` branch for the most up to date instructions.* For example from 6.2.0 to 6.2.1, also see the [semantic versioning specification](http://semver.org/). diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md index d8476fb3457..f62a53d3340 100644 --- a/doc/update/upgrader.md +++ b/doc/update/upgrader.md @@ -24,7 +24,7 @@ If you have local changes to your GitLab repository the script will stash them a ## 2. Run GitLab upgrade tool -Note: GitLab 7.9 adds nodejs as a dependency. GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX). GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) +Note: GitLab 7.9 adds `nodejs` as a dependency. GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX). GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) # Starting with GitLab version 7.0 upgrader script has been moved to bin directory cd /home/git/gitlab diff --git a/features/dashboard/new_project.feature b/features/dashboard/new_project.feature new file mode 100644 index 00000000000..431dc4ccfcb --- /dev/null +++ b/features/dashboard/new_project.feature @@ -0,0 +1,13 @@ +@dashboard +Feature: New Project +Background: + Given I sign in as a user + And I own project "Shop" + And I visit dashboard page + + @javascript + Scenario: I should see New projects page + Given I click "New project" link + Then I see "New project" page + When I click on "Import project from GitHub" + Then I see instructions on how to import from GitHub diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb new file mode 100644 index 00000000000..5e588ceb780 --- /dev/null +++ b/features/steps/dashboard/new_project.rb @@ -0,0 +1,27 @@ +class Spinach::Features::NewProject < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + + step 'I click "New project" link' do + click_link "New project" + end + + step 'I see "New project" page' do + page.should have_content("Project path") + end + + step 'I click on "Import project from GitHub"' do + first('.how_to_import_link').click + end + + step 'I see instructions on how to import from GitHub' do + github_modal = first('.modal-body') + github_modal.should be_visible + github_modal.should have_content "To enable importing projects from GitHub" + + all('.modal-body').each do |element| + element.should_not be_visible unless element == github_modal + end + end +end diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index bfbfe7af199..791982d16c3 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -11,6 +11,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps fill_in "user_linkedin", with: "testlinkedin" fill_in "user_twitter", with: "testtwitter" fill_in "user_website_url", with: "testurl" + fill_in "user_location", with: "Ukraine" click_button "Save changes" @user.reload end @@ -20,6 +21,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps @user.linkedin.should == 'testlinkedin' @user.twitter.should == 'testtwitter' @user.website_url.should == 'testurl' + find("#user_location").value.should == "Ukraine" end step 'I change my avatar' do diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index ab8db4e9837..c6087830b40 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -11,22 +11,27 @@ module Backup s[:tar_version] = tar_version tar_file = "#{s[:backup_created_at].to_i}_gitlab_backup.tar" - Dir.chdir(Gitlab.config.backup.path) + Dir.chdir(Gitlab.config.backup.path) do + File.open("#{Gitlab.config.backup.path}/backup_information.yml", + "w+") do |file| + file << s.to_yaml.gsub(/^---\n/,'') + end - File.open("#{Gitlab.config.backup.path}/backup_information.yml", "w+") do |file| - file << s.to_yaml.gsub(/^---\n/,'') - end + FileUtils.chmod_R(0700, %w{db uploads repositories}) - # create archive - $progress.print "Creating backup archive: #{tar_file} ... " - if Kernel.system('tar', '-cf', tar_file, *BACKUP_CONTENTS) - $progress.puts "done".green - else - puts "creating archive #{tar_file} failed".red - abort 'Backup failed' - end + # create archive + $progress.print "Creating backup archive: #{tar_file} ... " + orig_umask = File.umask(0077) + if Kernel.system('tar', '-cf', tar_file, *BACKUP_CONTENTS) + $progress.puts "done".green + else + puts "creating archive #{tar_file} failed".red + abort 'Backup failed' + end + File.umask(orig_umask) - upload(tar_file) + upload(tar_file) + end end def upload(tar_file) @@ -51,11 +56,13 @@ module Backup def cleanup $progress.print "Deleting tmp directories ... " - if Kernel.system('rm', '-rf', *BACKUP_CONTENTS) - $progress.puts "done".green - else - puts "deleting tmp directory failed".red - abort 'Backup failed' + BACKUP_CONTENTS.each do |dir| + if FileUtils.rm_rf(File.join(Gitlab.config.backup.path, dir)) + $progress.puts "done".green + else + puts "deleting tmp directory '#{dir}' failed".red + abort 'Backup failed' + end end end diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index e18bc804437..dfb2da9f84e 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -16,7 +16,7 @@ module Backup if project.empty_repo? $progress.puts "[SKIPPED]".cyan else - cmd = %W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all) + cmd = %W(tar -cf #{path_to_bundle(project)} -C #{path_to_repo(project)} .) output, status = Gitlab::Popen.popen(cmd) if status.zero? $progress.puts "[DONE]".green @@ -64,7 +64,8 @@ module Backup project.namespace.ensure_dir_exist if project.namespace if File.exists?(path_to_bundle(project)) - cmd = %W(git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)}) + FileUtils.mkdir_p(path_to_repo(project)) + cmd = %W(tar -xf #{path_to_bundle(project)} -C #{path_to_repo(project)}) else cmd = %W(git init --bare #{path_to_repo(project)}) end diff --git a/lib/gitlab/force_push_check.rb b/lib/gitlab/force_push_check.rb index eae9773a067..fdb6a35c78d 100644 --- a/lib/gitlab/force_push_check.rb +++ b/lib/gitlab/force_push_check.rb @@ -3,11 +3,12 @@ module Gitlab def self.force_push?(project, oldrev, newrev) return false if project.empty_repo? - if oldrev != Gitlab::Git::BLANK_SHA && newrev != Gitlab::Git::BLANK_SHA + # Created or deleted branch + if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) + false + else missed_refs, _ = Gitlab::Popen.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})) missed_refs.split("\n").size > 0 - else - false end end end diff --git a/lib/gitlab/middleware/timeout.rb b/lib/gitlab/middleware/timeout.rb deleted file mode 100644 index 015600392b9..00000000000 --- a/lib/gitlab/middleware/timeout.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Gitlab - module Middleware - class Timeout < Rack::Timeout - GRACK_REGEX = /[-\/\w\.]+\.git\//.freeze - - def call(env) - return @app.call(env) if env['PATH_INFO'] =~ GRACK_REGEX - - super - end - end - end -end diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index 694a30db5df..f8da452e4c0 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -21,7 +21,7 @@ module Gitlab # total_commits_count: Fixnum # } # - def build(project, user, oldrev, newrev, ref, commits = []) + def build(project, user, oldrev, newrev, ref, commits = [], message = nil) # Total commits count commits_count = commits.size @@ -42,6 +42,7 @@ module Gitlab after: newrev, ref: ref, checkout_sha: checkout_sha(project.repository, newrev, ref), + message: message, user_id: user.id, user_name: user.name, user_email: user.email, @@ -71,7 +72,8 @@ module Gitlab end def checkout_sha(repository, newrev, ref) - if newrev != Gitlab::Git::BLANK_SHA && Gitlab::Git.tag_ref?(ref) + # Find sha for tag, except when it was deleted. + if Gitlab::Git.tag_ref?(ref) && !Gitlab::Git.blank_ref?(newrev) tag_name = Gitlab::Git.ref_name(ref) tag = repository.find_tag(tag_name) diff --git a/lib/tasks/brakeman.rake b/lib/tasks/brakeman.rake index abcb5f0ae46..3a225801ff2 100644 --- a/lib/tasks/brakeman.rake +++ b/lib/tasks/brakeman.rake @@ -1,7 +1,7 @@ desc 'Security check via brakeman' task :brakeman do if system("brakeman --skip-files lib/backup/repository.rb -w3 -z") - exit 0 + puts 'Security check succeed' else puts 'Security check failed' exit 1 diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake index 189ad6090a4..3c9802a0be4 100644 --- a/lib/tasks/gitlab/cleanup.rake +++ b/lib/tasks/gitlab/cleanup.rake @@ -90,13 +90,14 @@ namespace :gitlab do warn_user_is_not_gitlab block_flag = ENV['BLOCK'] - User.ldap.each do |ldap_user| - print "#{ldap_user.name} (#{ldap_user.extern_uid}) ..." - if Gitlab::LDAP::Access.allowed?(ldap_user) + User.find_each do |user| + next unless user.ldap_user? + print "#{user.name} (#{user.ldap_identity.extern_uid}) ..." + if Gitlab::LDAP::Access.allowed?(user) puts " [OK]".green else if block_flag - ldap_user.block! unless ldap_user.blocked? + user.block! unless user.blocked? puts " [BLOCKED]".red else puts " [NOT IN LDAP]".yellow diff --git a/public/503.html b/public/503.html deleted file mode 100644 index efdae0f512d..00000000000 --- a/public/503.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>Page took too long to load (503)</title> - <link href="/static.css" media="screen" rel="stylesheet" type="text/css" /> -</head> -<body> - <h1>503</h1> - <h3>Page took too long to load.</h3> - <hr/> - <p>Please contact your GitLab administrator if this problem persists.</p> -</body> -</html> diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 7e524aa95cf..b3f4bb5aeda 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -140,4 +140,18 @@ describe Gitlab::ReferenceExtractor do expect(extracted[0][1].message).to eq(commit.message) end end + + context 'with a project with an underscore' do + let(:project) { create(:project, path: 'test_project') } + let(:issue) { create(:issue, project: project) } + + it 'handles project issue references' do + subject.analyze("this refers issue #{project.path_with_namespace}##{issue.iid}", + project) + extracted = subject.issues_for(project) + expect(extracted.size).to eq(1) + expect(extracted).to eq([issue]) + end + + end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 17cb439c90e..a7bf5081d5b 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -182,14 +182,14 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to match(/Status changed to #{status}/) } + it { is_expected.to eq("Status changed to #{status}") } end it 'appends a back-reference if a closing mentionable is supplied' do commit = double('commit', gfm_reference: 'commit 123456') n = Note.create_status_change_note(thing, project, author, status, commit) - expect(n.note).to match(/Status changed to #{status} by commit 123456/) + expect(n.note).to eq("Status changed to #{status} by commit 123456") end end @@ -197,7 +197,7 @@ describe Note do let(:project) { create(:project) } let(:thing) { create(:issue, project: project) } let(:author) { create(:user) } - let(:assignee) { create(:user) } + let(:assignee) { create(:user, username: "assigned_user") } subject { Note.create_assignee_change_note(thing, project, author, assignee) } @@ -227,7 +227,7 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to match(/Reassigned to @#{assignee.username}/) } + it { is_expected.to eq('Reassigned to @assigned_user') } end context 'assignee is removed' do @@ -235,11 +235,95 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to match(/Assignee removed/) } + it { is_expected.to eq('Assignee removed') } end end end + describe '#create_labels_change_note' do + let(:project) { create(:project) } + let(:thing) { create(:issue, project: project) } + let(:author) { create(:user) } + let(:label1) { create(:label) } + let(:label2) { create(:label) } + let(:added_labels) { [label1, label2] } + let(:removed_labels) { [] } + + subject { Note.create_labels_change_note(thing, project, author, added_labels, removed_labels) } + + context 'creates and saves a Note' do + it { is_expected.to be_a Note } + + describe '#id' do + subject { super().id } + it { is_expected.not_to be_nil } + end + end + + describe '#noteable' do + subject { super().noteable } + it { is_expected.to eq(thing) } + end + + describe '#project' do + subject { super().project } + it { is_expected.to eq(thing.project) } + end + + describe '#author' do + subject { super().author } + it { is_expected.to eq(author) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("Added ~#{label1.id} ~#{label2.id} labels") } + end + + context 'label is removed' do + let(:added_labels) { [label1] } + let(:removed_labels) { [label2] } + + describe '#note' do + subject { super().note } + it { is_expected.to eq("Added ~#{label1.id} and removed ~#{label2.id} labels") } + end + end + end + + describe '#create_milestone_change_note' do + let(:project) { create(:project) } + let(:thing) { create(:issue, project: project) } + let(:milestone) { create(:milestone, project: project, title: "first_milestone") } + let(:author) { create(:user) } + + subject { Note.create_milestone_change_note(thing, project, author, milestone) } + + context 'creates and saves a Note' do + it { is_expected.to be_a Note } + + describe '#id' do + subject { super().id } + it { is_expected.not_to be_nil } + end + end + + describe '#project' do + subject { super().project } + it { is_expected.to eq(thing.project) } + end + + describe '#author' do + subject { super().author } + it { is_expected.to eq(author) } + end + + describe '#note' do + subject { super().note } + it { is_expected.to eq("Milestone changed to first_milestone") } + end + end + describe '#create_cross_reference_note' do let(:project) { create(:project) } let(:author) { create(:user) } @@ -272,7 +356,7 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to eq("_mentioned in merge request !#{mergereq.iid}_") } + it { is_expected.to eq("mentioned in merge request !#{mergereq.iid}") } end end @@ -288,7 +372,7 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to eq("_mentioned in commit #{commit.sha}_") } + it { is_expected.to eq("mentioned in commit #{commit.sha}") } end end @@ -309,7 +393,7 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to eq("_mentioned in issue ##{issue.iid}_") } + it { is_expected.to eq("mentioned in issue ##{issue.iid}") } end end @@ -330,7 +414,7 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to eq("_mentioned in merge request !#{mergereq.iid}_") } + it { is_expected.to eq("mentioned in merge request !#{mergereq.iid}") } end end @@ -362,7 +446,7 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to eq("_mentioned in issue ##{issue.iid}_") } + it { is_expected.to eq("mentioned in issue ##{issue.iid}") } end end @@ -389,7 +473,7 @@ describe Note do describe '#note' do subject { super().note } - it { is_expected.to eq("_mentioned in commit #{parent_commit.id}_") } + it { is_expected.to eq("mentioned in commit #{parent_commit.id}") } end end end @@ -421,6 +505,41 @@ describe Note do it { expect(Note.cross_reference_exists?(commit0, commit1)).to be_truthy } it { expect(Note.cross_reference_exists?(commit1, commit0)).to be_falsey } end + + context 'legacy note with Markdown emphasis' do + let(:issue2) { create :issue, project: project } + let!(:note) do + create :note, system: true, noteable_id: issue2.id, + noteable_type: "Issue", note: "_mentioned in issue " \ + "#{issue.project.path_with_namespace}##{issue.iid}_" + end + + it 'detects if a mentionable with emphasis has been mentioned' do + expect(Note.cross_reference_exists?(issue2, issue)).to be_truthy + end + end + end + + describe '#cross_references_with_underscores?' do + let(:project) { create :project, path: "first_project" } + let(:second_project) { create :project, path: "second_project" } + + let(:author) { create :user } + let(:issue0) { create :issue, project: project } + let(:issue1) { create :issue, project: second_project } + let!(:note) { Note.create_cross_reference_note(issue0, issue1, author, project) } + + it 'detects if a mentionable has already been mentioned' do + expect(Note.cross_reference_exists?(issue0, issue1)).to be_truthy + end + + it 'detects if a mentionable has not already been mentioned' do + expect(Note.cross_reference_exists?(issue1, issue0)).to be_falsey + end + + it 'detects that text has underscores' do + expect(note.note).to eq("mentioned in issue #{second_project.path_with_namespace}##{issue1.iid}") + end end describe '#system?' do @@ -429,6 +548,8 @@ describe Note do let(:other) { create(:issue, project: project) } let(:author) { create(:user) } let(:assignee) { create(:user) } + let(:label) { create(:label) } + let(:milestone) { create(:milestone) } it 'should recognize user-supplied notes as non-system' do @note = create(:note_on_issue) @@ -449,6 +570,16 @@ describe Note do @note = Note.create_assignee_change_note(issue, project, author, assignee) expect(@note).to be_system end + + it 'should identify label-change notes as system notes' do + @note = Note.create_labels_change_note(issue, project, author, [label], []) + expect(@note).to be_system + end + + it 'should identify milestone-change notes as system notes' do + @note = Note.create_milestone_change_note(issue, project, author, milestone) + expect(@note).to be_system + end end describe :authorization do diff --git a/spec/models/project_services/buildbox_service_spec.rb b/spec/models/project_services/buildbox_service_spec.rb index 39d7df54cf0..fcbf3e45b9a 100644 --- a/spec/models/project_services/buildbox_service_spec.rb +++ b/spec/models/project_services/buildbox_service_spec.rb @@ -59,7 +59,7 @@ describe BuildboxService do describe :build_page do it 'returns the correct build page' do - expect(@service.build_page('2ab7834c')).to eq( + expect(@service.build_page('2ab7834c', nil)).to eq( 'https://buildbox.io/account-name/example-project/builds?commit=2ab7834c' ) end diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index 8bfb19e524b..610f33c5823 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -39,11 +39,11 @@ describe GitlabCiService do end describe :commit_status_path do - it { expect(@service.commit_status_path("2ab7834c")).to eq("http://ci.gitlab.org/projects/2/commits/2ab7834c/status.json?token=verySecret")} + it { expect(@service.commit_status_path("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c/status.json?token=verySecret")} end describe :build_page do - it { expect(@service.build_page("2ab7834c")).to eq("http://ci.gitlab.org/projects/2/commits/2ab7834c")} + it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c")} end end end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index f3fd805783f..fceb7668cac 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -78,6 +78,47 @@ describe WikiPage do end end + describe "dot in the title" do + let(:title) { 'Index v1.2.3' } + + before do + @wiki_attr = {title: title, content: "Home Page", format: "markdown"} + end + + describe "#create" do + after do + destroy_page(title) + end + + context "with valid attributes" do + it "saves the wiki page" do + subject.create(@wiki_attr) + expect(wiki.find_page(title)).not_to be_nil + end + + it "returns true" do + expect(subject.create(@wiki_attr)).to eq(true) + end + end + end + + describe "#update" do + before do + create_page(title, "content") + @page = wiki.find_page(title) + end + + it "updates the content of the page" do + @page.update("new content") + @page = wiki.find_page(title) + end + + it "returns true" do + expect(@page.update("more content")).to be_truthy + end + end + end + describe "#update" do before do create_page("Update", "content") diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 1b1e3ca5f8b..aa9b15dd9ec 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -145,11 +145,6 @@ describe GitPushService do expect(project).to receive(:execute_hooks) service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master') end - - it "when pushing tags" do - expect(project).not_to receive(:execute_hooks) - service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0') - end end end diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index fcf462edbfc..a050fdf6c0e 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -1,32 +1,39 @@ require 'spec_helper' describe GitTagPushService do + include RepoHelpers + let (:user) { create :user } let (:project) { create :project } let (:service) { GitTagPushService.new } before do - @ref = 'refs/tags/super-tag' - @oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81' - @newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb' + @oldrev = Gitlab::Git::BLANK_SHA + @newrev = "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" # gitlab-test: git rev-parse refs/tags/v1.1.0 + @ref = 'refs/tags/v1.1.0' end - describe 'Git Tag Push Data' do + describe "Git Tag Push Data" do before do service.execute(project, user, @oldrev, @newrev, @ref) @push_data = service.push_data + @tag_name = Gitlab::Git.ref_name(@ref) + @tag = project.repository.find_tag(@tag_name) + @commit = project.repository.commit(@tag.target) end subject { @push_data } + it { is_expected.to include(object_kind: 'tag_push') } it { is_expected.to include(ref: @ref) } it { is_expected.to include(before: @oldrev) } it { is_expected.to include(after: @newrev) } + it { is_expected.to include(message: @tag.message) } it { is_expected.to include(user_id: user.id) } it { is_expected.to include(user_name: user.name) } it { is_expected.to include(project_id: project.id) } - context 'With repository data' do + context "with repository data" do subject { @push_data[:repository] } it { is_expected.to include(name: project.name) } @@ -34,6 +41,41 @@ describe GitTagPushService do it { is_expected.to include(description: project.description) } it { is_expected.to include(homepage: project.web_url) } end + + context "with commits" do + subject { @push_data[:commits] } + + it { is_expected.to be_an(Array) } + it 'has 1 element' do + expect(subject.size).to eq(1) + end + + context "the commit" do + subject { @push_data[:commits].first } + + it { is_expected.to include(id: @commit.id) } + it { is_expected.to include(message: @commit.safe_message) } + it { is_expected.to include(timestamp: @commit.date.xmlschema) } + it do + is_expected.to include( + url: [ + Gitlab.config.gitlab.url, + project.namespace.to_param, + project.to_param, + 'commit', + @commit.id + ].join('/') + ) + end + + context "with a author" do + subject { @push_data[:commits].first[:author] } + + it { is_expected.to include(name: @commit.author_name) } + it { is_expected.to include(email: @commit.author_email) } + end + end + end end describe "Web Hooks" do diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 60942cc95fc..8a411b7720a 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -10,17 +10,17 @@ describe 'gitlab:app namespace rake task' do Rake::Task.define_task :environment end + def run_rake_task(task_name) + Rake::Task[task_name].reenable + Rake.application.invoke_task task_name + end + describe 'backup_restore' do before do # avoid writing task output to spec progress allow($stdout).to receive :write end - let :run_rake_task do - Rake::Task["gitlab:backup:restore"].reenable - Rake.application.invoke_task "gitlab:backup:restore" - end - context 'gitlab version' do before do Dir.stub glob: [] @@ -36,7 +36,9 @@ describe 'gitlab:app namespace rake task' do it 'should fail on mismatch' do YAML.stub load_file: {gitlab_version: "not #{gitlab_version}" } - expect { run_rake_task }.to raise_error SystemExit + expect { run_rake_task('gitlab:backup:restore') }.to( + raise_error SystemExit + ) end it 'should invoke restoration on mach' do @@ -44,9 +46,56 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:repo:restore"]).to receive :invoke expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke - expect { run_rake_task }.to_not raise_error + expect { run_rake_task('gitlab:backup:restore') }.to_not raise_error end end end # backup_restore task + + describe 'backup_create' do + def tars_glob + Dir.glob(File.join(Gitlab.config.backup.path, '*_gitlab_backup.tar')) + end + + before :all do + # Record the existing backup tars so we don't touch them + existing_tars = tars_glob + + # Redirect STDOUT and run the rake task + orig_stdout = $stdout + $stdout = StringIO.new + run_rake_task('gitlab:backup:create') + $stdout = orig_stdout + + @backup_tar = (tars_glob - existing_tars).first + end + + after :all do + FileUtils.rm(@backup_tar) + end + + it 'should set correct permissions on the tar file' do + expect(File.exist?(@backup_tar)).to be_truthy + expect(File::Stat.new(@backup_tar).mode.to_s(8)).to eq('100600') + end + + it 'should set correct permissions on the tar contents' do + tar_contents, exit_status = Gitlab::Popen.popen( + %W{tar -tvf #{@backup_tar} db uploads repositories} + ) + expect(exit_status).to eq(0) + expect(tar_contents).to match('db/') + expect(tar_contents).to match('uploads/') + expect(tar_contents).to match('repositories/') + expect(tar_contents).not_to match(/^.{4,9}[rwx]/) + end + + it 'should delete temp directories' do + temp_dirs = Dir.glob( + File.join(Gitlab.config.backup.path, '{db,repositories,uploads}') + ) + + expect(temp_dirs).to be_empty + end + end # backup_create task end # gitlab:app namespace |