diff options
24 files changed, 144 insertions, 28 deletions
diff --git a/CHANGELOG b/CHANGELOG index 5b0d38b9898..594ddf3f1ce 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,8 @@ v 6.7.0 - Public groups (Jason Hollingsworth) - Cleaner headers in Notification Emails (Pierre de La Morinerie) - Blob and tree gfm links to anchors work + - Piwik Integration (Sebastian Winkler) + - Show contribution guide link for new issue form (Jeroen van Baarsen) v 6.6.2 - Fix 500 error on branch/tag create or remove via UI @@ -25,7 +27,7 @@ v 6.6.0 - Remove snippet expiration - Mobile UI improvements (Drew Blessing) - Fix block/remove UI for admin::users#show page - - Show users' group membership on users' activity page + - Show users' group membership on users' activity page (Robert Djurasaj) - User pages are visible without login if user is authorized to a public project - Markdown rendered headers have id derived from their name and link to their id - Improve application to work faster with large groups (100+ members) @@ -15,6 +15,9 @@ gem 'rails-observers' gem 'actionpack-page_caching' gem 'actionpack-action_caching' +# Default values for AR models +gem "default_value_for", "~> 3.0.0" + # Supported DBs gem "mysql2", group: :mysql gem "pg", group: :postgres @@ -29,7 +32,7 @@ gem 'omniauth-github' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '~> 5.4.0' +gem "gitlab_git", '~> 5.5.0' # Ruby/Rack Git Smart-HTTP Server Handler gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' @@ -164,7 +167,7 @@ group :development do gem "annotate", "~> 2.6.0.beta2" gem "letter_opener" gem 'quiet_assets', '~> 1.0.1' - gem 'rack-mini-profiler' + gem 'rack-mini-profiler', require: false # Better errors handler gem 'better_errors' diff --git a/Gemfile.lock b/Gemfile.lock index e55bd26e040..2cf0929fe79 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -101,6 +101,8 @@ GEM daemons (1.1.9) database_cleaner (1.2.0) debug_inspector (0.0.2) + default_value_for (3.0.0) + activerecord (>= 3.2.0, < 5.0) descendants_tracker (0.0.3) devise (3.0.4) bcrypt-ruby (~> 3.0) @@ -179,7 +181,7 @@ GEM charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.4) mime-types (~> 1.19) - gitlab_git (5.4.0) + gitlab_git (5.5.0) activesupport (~> 4.0.0) charlock_holmes (~> 0.6.9) gitlab-grit (~> 2.6.1) @@ -570,6 +572,7 @@ DEPENDENCIES coveralls d3_rails (~> 3.1.4) database_cleaner + default_value_for (~> 3.0.0) devise (= 3.0.4) devise-async (= 0.8.0) email_spec @@ -587,7 +590,7 @@ DEPENDENCIES gitlab-gollum-lib (~> 1.1.0) gitlab-grack (~> 2.0.0.pre) gitlab-linguist (~> 3.0.0) - gitlab_git (~> 5.4.0) + gitlab_git (~> 5.5.0) gitlab_meta (= 6.0) gitlab_omniauth-ldap (= 1.0.4) gon (~> 5.0.0) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bb25327bc2d..9ed46c23942 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -210,4 +210,8 @@ class ApplicationController < ActionController::Base devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me) } devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :name, :password, :password_confirmation) } end + + def hexdigest(string) + Digest::SHA1.hexdigest string + end end diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb index c54b757d13c..ff5206b6fa1 100644 --- a/app/controllers/projects/edit_tree_controller.rb +++ b/app/controllers/projects/edit_tree_controller.rb @@ -2,6 +2,8 @@ class Projects::EditTreeController < Projects::BaseTreeController before_filter :require_branch_head before_filter :blob before_filter :authorize_push! + before_filter :from_merge_request + before_filter :after_edit_path def show @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha @@ -13,15 +15,11 @@ class Projects::EditTreeController < Projects::BaseTreeController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - # If blob edit was initiated from merge request page - from_merge_request = MergeRequest.find_by(id: params[:from_merge_request_id]) - if from_merge_request from_merge_request.reload_code - redirect_to diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) - else - redirect_to project_blob_path(@project, @id) end + + redirect_to after_edit_path else flash[:alert] = result[:error] render :show @@ -33,4 +31,19 @@ class Projects::EditTreeController < Projects::BaseTreeController def blob @blob ||= @repository.blob_at(@commit.id, @path) end + + def after_edit_path + @after_edit_path ||= + if from_merge_request + diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) + + "#file-path-#{hexdigest(@path)}" + else + project_blob_path(@project, @id) + end + end + + def from_merge_request + # If blob edit was initiated from merge request page + @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id]) + end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 6f7ea9c96a4..51509ecda35 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -60,7 +60,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController def new @merge_request = MergeRequest.new(params[:merge_request]) @merge_request.source_project = @project unless @merge_request.source_project - @merge_request.target_project = @project unless @merge_request.target_project + @merge_request.target_project ||= (@project.forked_from_project || @project) + @target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names + + @merge_request.target_branch ||= @merge_request.target_project.default_branch + @source_project = @merge_request.source_project @merge_request end @@ -162,7 +166,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def ci_status - status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) + status = @merge_request.source_project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) response = {status: status} render json: response diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 62f061bb079..b8285d43302 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,8 +1,9 @@ module MergeRequestsHelper def new_mr_path_from_push_event(event) + target_project = event.project.forked_from_project || event.project new_project_merge_request_path( event.project, - new_mr_from_push_event(event, event.project) + new_mr_from_push_event(event, target_project) ) end diff --git a/app/models/key.rb b/app/models/key.rb index 79f7bbd2590..29a76f53f3d 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -53,7 +53,7 @@ class Key < ActiveRecord::Base Tempfile.open('gitlab_key_file') do |file| file.puts key file.rewind - cmd_output, cmd_status = popen("ssh-keygen -lf #{file.path}", '/tmp') + cmd_output, cmd_status = popen(%W(ssh-keygen -lf #{file.path}), '/tmp') end if cmd_status.zero? diff --git a/app/models/repository.rb b/app/models/repository.rb index a2317fd592f..35ec84f1651 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -134,6 +134,7 @@ class Repository Rails.cache.delete(cache_key(:commit_count)) Rails.cache.delete(cache_key(:graph_log)) Rails.cache.delete(cache_key(:readme)) + Rails.cache.delete(cache_key(:contribution_guide)) end def graph_log @@ -167,6 +168,12 @@ class Repository end end + def contribution_guide + Rails.cache.fetch(cache_key(:contribution_guide)) do + tree(:head).contribution_guide + end + end + def head_commit commit(self.root_ref) end diff --git a/app/models/service.rb b/app/models/service.rb index 26060d00b02..f7e440dcc81 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -19,6 +19,8 @@ # To add new service you should build a class inherited from Service # and implement a set of methods class Service < ActiveRecord::Base + default_value_for :active, false + attr_accessible :title, :token, :type, :active, :api_key belongs_to :project diff --git a/app/models/tree.rb b/app/models/tree.rb index 4f866f1a33d..f1077772ea9 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -1,5 +1,5 @@ class Tree - attr_accessor :entries, :readme + attr_accessor :entries, :readme, :contribution_guide def initialize(repository, sha, path = '/') path = '/' if path.blank? @@ -10,6 +10,11 @@ class Tree readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name) @readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path) end + + if contribution_tree = @entries.find(&:contribution?) + contribution_path = path == '/' ? contribution_tree.name : File.join(path, contribution_tree.name) + @contribution_guide = Gitlab::Git::Blob.find(git_repo, sha, contribution_path) + end end def trees diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 5723250151a..9ba20f1347d 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -12,6 +12,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> = render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id') + = render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id') -# Atom feed - if current_user diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml new file mode 100644 index 00000000000..135e8daca26 --- /dev/null +++ b/app/views/layouts/_piwik.html.haml @@ -0,0 +1,12 @@ +:javascript + var _paq = _paq || []; + _paq.push(["trackPageView"]); + _paq.push(["enableLinkTracking"]); + + (function() { + var u=(("https:" == document.location.protocol) ? "https" : "http") + "://#{extra_config.piwik_url}/"; + _paq.push(["setTrackerUrl", u+"piwik.php"]); + _paq.push(["setSiteId", "#{extra_config.piwik_site_id}"]); + var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript"; + g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s); + })(); diff --git a/app/views/projects/commits/_diffs.html.haml b/app/views/projects/commits/_diffs.html.haml index da585efd0af..dd287fcc153 100644 --- a/app/views/projects/commits/_diffs.html.haml +++ b/app/views/projects/commits/_diffs.html.haml @@ -45,7 +45,7 @@ - file = project.repository.blob_at(@commit.parent_id, diff.old_path) unless file - next unless file .diff-file{id: "diff-#{i}"} - .diff-header + .diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"} - if diff.deleted_file %span= diff.old_path diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml index 96c00ab3661..3f2e98f3a7f 100644 --- a/app/views/projects/edit_tree/show.html.haml +++ b/app/views/projects/edit_tree/show.html.haml @@ -11,7 +11,7 @@ %strong= @ref %span.options .btn-group.tree-btn-group - = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message } + = link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message } .file-content.code %pre#editor= @blob.data @@ -29,7 +29,7 @@ .message to branch %strong= @ref - = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-cancel", data: { confirm: leave_edit_message} + = link_to "Cancel", @after_edit_path, class: "btn btn-cancel", data: { confirm: leave_edit_message} :javascript ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace") diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index f725db57ad1..dd091302c8e 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,6 +1,10 @@ %div.issue-form-holder %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" %hr + - if @repository.contribution_guide && !@issue.persisted? + - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) + .alert.alert-info.col-sm-10.col-sm-offset-2 + ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe = form_for [@project, @issue], html: { class: 'form-horizontal issue-form' } do |f| -if @issue.errors.any? .alert.alert-danger diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 9502ff95d8e..51fa29ddcbe 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,3 +1,7 @@ +- if @repository.contribution_guide && !@merge_request.persisted? + - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) + .alert.alert-info.col-sm-10.col-sm-offset-2 + ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe = form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f| -if @merge_request.errors.any? .alert.alert-danger @@ -25,7 +29,7 @@ .clearfix .pull-left - projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project] - = f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace'), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? }) + = f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? }) .pull-left = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'}) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index ce57465d687..a40ce7212fe 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -217,6 +217,10 @@ production: &base ## Google analytics. Uncomment if you want it # google_analytics_id: '_your_tracking_id' + ## Piwik analytics. + # piwik_url: '_your_piwik_url' + # piwik_site_id: '_your_piwik_site_id' + ## Text under sign-in page (Markdown enabled) # sign_in_text: | #  @@ -227,6 +231,11 @@ development: test: <<: *base + gravatar: + enabled: true + gitlab: + host: localhost + port: 80 issues_tracker: redmine: title: "Redmine" diff --git a/config/initializers/6_rack_profiler.rb b/config/initializers/6_rack_profiler.rb new file mode 100644 index 00000000000..a7ee3c59822 --- /dev/null +++ b/config/initializers/6_rack_profiler.rb @@ -0,0 +1,6 @@ +if Rails.env == 'development' + require 'rack-mini-profiler' + + # initialization is skipped so trigger it + Rack::MiniProfilerRails.initialize!(Rails.application) +end diff --git a/features/project/forked_merge_requests.feature b/features/project/forked_merge_requests.feature index 966905645a2..2d94b98c90b 100644 --- a/features/project/forked_merge_requests.feature +++ b/features/project/forked_merge_requests.feature @@ -32,3 +32,9 @@ Feature: Project Forked Merge Requests And I fill out an invalid "Merge Request On Forked Project" merge request And I submit the merge request Then I should see validation errors + + @javascript + Scenario: Merge request should target fork repository by default + Given I visit project "Forked Shop" merge requests page + And I click link "New Merge Request" + Then the target repository should be the original repository
\ No newline at end of file diff --git a/features/steps/project/project_forked_merge_requests.rb b/features/steps/project/project_forked_merge_requests.rb index 4cc99f8af55..df69cb75437 100644 --- a/features/steps/project/project_forked_merge_requests.rb +++ b/features/steps/project/project_forked_merge_requests.rb @@ -159,8 +159,11 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps step 'I fill out an invalid "Merge Request On Forked Project" merge request' do #If this isn't filled in the rest of the validations won't be triggered fill_in "merge_request_title", with: "Merge Request On Forked Project" + + select "Select branch", from: "merge_request_target_branch" + find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s - find(:select, "merge_request_target_project_id", {}).value.should == @forked_project.id.to_s + find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s find(:select, "merge_request_source_branch", {}).value.should == "" find(:select, "merge_request_target_branch", {}).value.should == "" end @@ -168,7 +171,10 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps step 'I should see validation errors' do page.should have_content "Source branch can't be blank" page.should have_content "Target branch can't be blank" - page.should have_content "Branch conflict You can not use same project/branch for source and target" + end + + step 'the target repository should be the original repository' do + page.should have_select("merge_request_target_project_id", selected: project.path_with_namespace) end def project diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb index 5283cf0b821..e2fbafb3899 100644 --- a/lib/gitlab/popen.rb +++ b/lib/gitlab/popen.rb @@ -1,8 +1,16 @@ require 'fileutils' +require 'open3' module Gitlab module Popen - def popen(cmd, path) + extend self + + def popen(cmd, path=nil) + unless cmd.is_a?(Array) + raise "System commands must be given as an array of strings" + end + + path ||= Dir.pwd vars = { "PWD" => path } options = { chdir: path } @@ -12,10 +20,10 @@ module Gitlab @cmd_output = "" @cmd_status = 0 - Open3.popen3(vars, cmd, options) do |stdin, stdout, stderr, wait_thr| - @cmd_status = wait_thr.value.exitstatus + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| @cmd_output << stdout.read @cmd_output << stderr.read + @cmd_status = wait_thr.value.exitstatus end return @cmd_output, @cmd_status diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index 353c3024aad..bcf3012bd92 100644 --- a/lib/gitlab/satellite/satellite.rb +++ b/lib/gitlab/satellite/satellite.rb @@ -33,7 +33,7 @@ module Gitlab end def create - output, status = popen("git clone #{project.repository.path_to_repo} #{path}", + output, status = popen(%W(git clone -- #{project.repository.path_to_repo} #{path}), Gitlab.config.satellites.path) log("PID: #{project.id}: git clone #{project.repository.path_to_repo} #{path}") diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb index 4791be41613..76d506eb3c0 100644 --- a/spec/lib/gitlab/popen_spec.rb +++ b/spec/lib/gitlab/popen_spec.rb @@ -10,7 +10,7 @@ describe 'Gitlab::Popen', no_db: true do context 'zero status' do before do - @output, @status = @klass.new.popen('ls', path) + @output, @status = @klass.new.popen(%W(ls), path) end it { @status.should be_zero } @@ -19,11 +19,27 @@ describe 'Gitlab::Popen', no_db: true do context 'non-zero status' do before do - @output, @status = @klass.new.popen('cat NOTHING', path) + @output, @status = @klass.new.popen(%W(cat NOTHING), path) end it { @status.should == 1 } it { @output.should include('No such file or directory') } end + + context 'unsafe string command' do + it 'raises an error when it gets called with a string argument' do + expect { @klass.new.popen('ls', path) }.to raise_error + end + end + + context 'without a directory argument' do + before do + @output, @status = @klass.new.popen(%W(ls)) + end + + it { @status.should be_zero } + it { @output.should include('spec') } + end + end |
