diff options
author | Douwe Maan <douwe@gitlab.com> | 2015-08-20 22:19:19 +0300 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2015-08-20 22:19:19 +0300 |
commit | 3d51a6d4351c6a15c92762f5710a967374d0c59b (patch) | |
tree | 10c5e87e0add51ef0bdcc9be42ac0176907a080c | |
parent | 8ec5fb138dde9937814ac138352177399d3e776d (diff) | |
parent | 8819007c83fdf1ac642836640a37cc541f6eddc6 (diff) |
Merge branch 'master' into reply-by-email
146 files changed, 1229 insertions, 1659 deletions
diff --git a/CHANGELOG b/CHANGELOG index 43302425f14..54f83e5aeac 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,13 @@ Please view this file on the master branch, on stable branches it's out of date. +v 8.0.0 (unreleased) + - Remove satellites + - Better performance for web editor (switched from satellites to rugged) + - Faster merge + - Ability to fetch merge requests from refs/merge-requests/:id + - Allow displaying of archived projects in the admin interface (Artem Sidorenko) + - Allow configuration of import sources for new projects (Artem Sidorenko) + v 7.14.0 (unreleased) - Update default robots.txt rules to disallow crawling of irrelevant pages (Ben Bodenmiller) - Fix redirection after sign in when using auto_sign_in_with_provider @@ -1 +1 @@ -7.14.0.pre +8.0.0.pre diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 5ab91261d75..995a2f24093 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -19,7 +19,7 @@ class @MergeRequestWidget when 'merged' location.reload() else - setTimeout(merge_request_widget.mergeInProgress, 3000) + setTimeout(merge_request_widget.mergeInProgress, 2000) dataType: 'json' getMergeStatus: -> diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index d36530169a9..bf5c7a8d75e 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -373,3 +373,23 @@ table { border-color: #EEE !important; } } + +.center-top-menu { + border-bottom: 1px solid #EEE; + list-style: none; + text-align: center; + padding-bottom: 15px; + margin-bottom: 15px; + + li { + display: inline-block; + + a { + padding: 10px; + } + + &.active a { + color: #666; + } + } +} diff --git a/app/assets/stylesheets/pages/explore.scss b/app/assets/stylesheets/pages/explore.scss index 9b92128624c..da06fe9954e 100644 --- a/app/assets/stylesheets/pages/explore.scss +++ b/app/assets/stylesheets/pages/explore.scss @@ -6,3 +6,11 @@ font-size: 30px; } } + +.explore-trending-block { + .lead { + line-height: 32px; + font-size: 18px; + margin-top: 10px; + } +} diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index c7c643db401..f38e07af84b 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -29,6 +29,15 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end end + import_sources = params[:application_setting][:import_sources] + if import_sources.nil? + params[:application_setting][:import_sources] = [] + else + import_sources.map! do |source| + source.to_str + end + end + params.require(:application_setting).permit( :default_projects_limit, :default_branch_protection, @@ -47,6 +56,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :version_check_enabled, :user_oauth_applications, restricted_visibility_levels: [], + import_sources: [] ) end end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index da5f5bb83fa..ae1de06b983 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -9,6 +9,7 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? @projects = @projects.with_push if params[:with_push].present? @projects = @projects.abandoned if params[:abandoned].present? + @projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3ce8dbc9407..12d439b0b31 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -20,7 +20,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception helper_method :abilities, :can?, :current_application_settings - helper_method :github_import_enabled?, :gitlab_import_enabled?, :bitbucket_import_enabled? + helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :git_import_enabled? rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -298,15 +298,43 @@ class ApplicationController < ActionController::Base @issuable_finder.execute end + def import_sources_enabled? + !current_application_settings.import_sources.empty? + end + def github_import_enabled? + current_application_settings.import_sources.include?('github') + end + + def github_import_configured? Gitlab::OAuth::Provider.enabled?(:github) end def gitlab_import_enabled? + request.host != 'gitlab.com' && current_application_settings.import_sources.include?('gitlab') + end + + def gitlab_import_configured? Gitlab::OAuth::Provider.enabled?(:gitlab) end def bitbucket_import_enabled? + current_application_settings.import_sources.include?('bitbucket') + end + + def bitbucket_import_configured? Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present? end + + def gitorious_import_enabled? + current_application_settings.import_sources.include?('gitorious') + end + + def google_code_import_enabled? + current_application_settings.import_sources.include?('google_code') + end + + def git_import_enabled? + current_application_settings.import_sources.include?('git') + end end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index e9bcb44f6b3..6c733c1ae4d 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -7,6 +7,7 @@ class Explore::ProjectsController < Explore::ApplicationController @tags = @projects.tags_on(:tags) @projects = @projects.tagged_with(params[:tag]) if params[:tag].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? + @projects = @projects.non_archived @projects = @projects.search(params[:search]) if params[:search].present? @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) @@ -14,6 +15,7 @@ class Explore::ProjectsController < Explore::ApplicationController def trending @trending_projects = TrendingProjectsFinder.new.execute(current_user) + @trending_projects = @trending_projects.non_archived @trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE) end diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 8a45dc8860d..71831c5380d 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -10,7 +10,8 @@ class HelpController < ApplicationController respond_to do |format| format.any(:markdown, :md, :html) do - path = Rails.root.join('doc', @category, "#{@file}.md") + # Note: We are purposefully NOT using `Rails.root.join` + path = File.join(Rails.root, 'doc', @category, "#{@file}.md") if File.exist?(path) @markdown = File.read(path) @@ -24,7 +25,8 @@ class HelpController < ApplicationController # Allow access to images in the doc folder format.any(:png, :gif, :jpeg) do - path = Rails.root.join('doc', @category, "#{@file}.#{params[:format]}") + # Note: We are purposefully NOT using `Rails.root.join` + path = File.join(Rails.root, 'doc', @category, "#{@file}.#{params[:format]}") if File.exist?(path) send_file(path, disposition: 'inline') diff --git a/app/controllers/import/gitorious_controller.rb b/app/controllers/import/gitorious_controller.rb index c121d2de7cb..f24cdb3709a 100644 --- a/app/controllers/import/gitorious_controller.rb +++ b/app/controllers/import/gitorious_controller.rb @@ -1,4 +1,5 @@ class Import::GitoriousController < Import::BaseController + before_action :verify_gitorious_import_enabled def new redirect_to client.authorize_url(callback_import_gitorious_url) @@ -40,4 +41,8 @@ class Import::GitoriousController < Import::BaseController @client ||= Gitlab::GitoriousImport::Client.new(session[:gitorious_repos]) end + def verify_gitorious_import_enabled + not_found! unless gitorious_import_enabled? + end + end diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb index 4aa6d28c9a8..82fadeb7e83 100644 --- a/app/controllers/import/google_code_controller.rb +++ b/app/controllers/import/google_code_controller.rb @@ -1,4 +1,5 @@ class Import::GoogleCodeController < Import::BaseController + before_action :verify_google_code_import_enabled before_action :user_map, only: [:new_user_map, :create_user_map] def new @@ -104,6 +105,10 @@ class Import::GoogleCodeController < Import::BaseController @client ||= Gitlab::GoogleCodeImport::Client.new(session[:google_code_dump]) end + def verify_google_code_import_enabled + not_found! unless google_code_import_enabled? + end + def user_map @user_map ||= begin user_map = client.user_map diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index b762518d377..100d3d3b317 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -13,27 +13,20 @@ class Projects::BlobController < Projects::ApplicationController before_action :commit, except: [:new, :create] before_action :blob, except: [:new, :create] before_action :from_merge_request, only: [:edit, :update] - before_action :after_edit_path, only: [:edit, :update] before_action :require_branch_head, only: [:edit, :update] + before_action :editor_variables, except: [:show, :preview, :diff] + before_action :after_edit_path, only: [:edit, :update] def new commit unless @repository.empty? end def create - file_path = File.join(@path, File.basename(params[:file_name])) - result = Files::CreateService.new( - @project, - current_user, - params.merge(new_branch: sanitized_new_branch_name), - @ref, - file_path - ).execute + result = Files::CreateService.new(@project, current_user, @commit_params).execute if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - ref = sanitized_new_branch_name.presence || @ref - redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(ref, file_path)) + redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) else flash[:alert] = result[:message] render :new @@ -48,22 +41,10 @@ class Projects::BlobController < Projects::ApplicationController end def update - result = Files::UpdateService. - new( - @project, - current_user, - params.merge(new_branch: sanitized_new_branch_name), - @ref, - @path - ).execute + result = Files::UpdateService.new(@project, current_user, @commit_params).execute if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - - if from_merge_request - from_merge_request.reload_code - end - redirect_to after_edit_path else flash[:alert] = result[:message] @@ -80,12 +61,11 @@ class Projects::BlobController < Projects::ApplicationController end def destroy - result = Files::DeleteService.new(@project, current_user, params, @ref, @path).execute + result = Files::DeleteService.new(@project, current_user, @commit_params).execute if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - redirect_to namespace_project_tree_path(@project.namespace, @project, - @ref) + redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch) else flash[:alert] = result[:message] render :show @@ -135,7 +115,6 @@ class Projects::BlobController < Projects::ApplicationController @id = params[:id] @ref, @path = extract_ref(@id) - rescue InvalidPathError not_found! end @@ -145,8 +124,8 @@ class Projects::BlobController < Projects::ApplicationController if from_merge_request diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + "#file-path-#{hexdigest(@path)}" - elsif sanitized_new_branch_name.present? - namespace_project_blob_path(@project.namespace, @project, File.join(sanitized_new_branch_name, @path)) + elsif @target_branch.present? + namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path)) else namespace_project_blob_path(@project.namespace, @project, @id) end @@ -160,4 +139,25 @@ class Projects::BlobController < Projects::ApplicationController def sanitized_new_branch_name @new_branch ||= sanitize(strip_tags(params[:new_branch])) end + + def editor_variables + @current_branch = @ref + @target_branch = (sanitized_new_branch_name || @ref) + + @file_path = + if action_name.to_s == 'create' + File.join(@path, File.basename(params[:file_name])) + else + @path + end + + @commit_params = { + file_path: @file_path, + current_branch: @current_branch, + target_branch: @target_branch, + commit_message: params[:commit_message], + file_content: params[:content], + file_content_encoding: params[:encoding] + } + end end diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index c5f085c236f..d9b3adae95b 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -13,13 +13,8 @@ class Projects::CompareController < Projects::ApplicationController base_ref = Addressable::URI.unescape(params[:from]) @ref = head_ref = Addressable::URI.unescape(params[:to]) - compare_result = CompareService.new.execute( - current_user, - @project, - head_ref, - @project, - base_ref - ) + compare_result = CompareService.new. + execute(@project, head_ref, @project, base_ref) @commits = compare_result.commits @diffs = compare_result.diffs diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index d1265198318..f3054881daf 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -1,9 +1,7 @@ -require 'gitlab/satellite/satellite' - class Projects::MergeRequestsController < Projects::ApplicationController before_action :module_enabled before_action :merge_request, only: [ - :edit, :update, :show, :diffs, :commits, :automerge, :automerge_check, + :edit, :update, :show, :diffs, :commits, :merge, :merge_check, :ci_status, :toggle_subscription ] before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits] @@ -137,7 +135,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end - def automerge_check + def merge_check if @merge_request.unchecked? @merge_request.check_if_can_be_merged end @@ -147,11 +145,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController render partial: "projects/merge_requests/widget/show.html.haml", layout: false end - def automerge + def merge return access_denied! unless @merge_request.can_be_merged_by?(current_user) - if @merge_request.automergeable? - AutoMergeWorker.perform_async(@merge_request.id, current_user.id, params) + if @merge_request.mergeable? + MergeWorker.perform_async(@merge_request.id, current_user.id, params) @status = true else @status = false diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 61d14383945..7d6b58ee21a 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -39,4 +39,21 @@ module ApplicationSettingsHelper end end end + + # Return a group of checkboxes that use Bootstrap's button plugin for a + # toggle button effect. + def import_sources_checkboxes(help_block_id) + Gitlab::ImportSources.options.map do |name, source| + checked = current_application_settings.import_sources.include?(source) + css_class = 'btn' + css_class += ' active' if checked + checkbox_name = 'application_setting[import_sources][]' + + label_tag(checkbox_name, class: css_class) do + check_box_tag(checkbox_name, source, checked, + autocomplete: 'off', + 'aria-describedby' => help_block_id) + name + end + end + end end diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb index 7616fe6bad8..0d291f9a87e 100644 --- a/app/helpers/explore_helper.rb +++ b/app/helpers/explore_helper.rb @@ -10,7 +10,7 @@ module ExploreHelper options = exist_opts.merge(options) - path = request.path + path = explore_projects_path path << "?#{options.to_param}" path end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 77727337f07..0e7d8065ac7 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -67,6 +67,14 @@ module TabHelper path.any? do |single_path| current_path?(single_path) end + elsif page = options.delete(:page) + unless page.respond_to?(:each) + page = [page] + end + + page.any? do |single_page| + current_page?(single_page) + end else c = options.delete(:controller) a = options.delete(:action) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 6d1ad82a262..8f27e35d723 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -22,10 +22,12 @@ # user_oauth_applications :boolean default(TRUE) # after_sign_out_path :string(255) # session_expire_delay :integer default(10080), not null +# import_sources :text # class ApplicationSetting < ActiveRecord::Base serialize :restricted_visibility_levels + serialize :import_sources serialize :restricted_signup_domains, Array attr_accessor :restricted_signup_domains_raw @@ -52,6 +54,16 @@ class ApplicationSetting < ActiveRecord::Base end end + validates_each :import_sources do |record, attr, value| + unless value.nil? + value.each do |source| + unless Gitlab::ImportSources.options.has_value?(source) + record.errors.add(attr, "'#{source}' is not a import source") + end + end + end + end + def self.current ApplicationSetting.last end @@ -70,7 +82,8 @@ class ApplicationSetting < ActiveRecord::Base session_expire_delay: Settings.gitlab['session_expire_delay'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], - restricted_signup_domains: Settings.gitlab['restricted_signup_domains'] + restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], + import_sources: ['github','bitbucket','gitlab','gitorious','google_code','git'] ) end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 324d1795ab4..467b90861f9 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -41,8 +41,6 @@ class MergeRequest < ActiveRecord::Base delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil - attr_accessor :should_remove_source_branch - # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests attr_accessor :allow_broken @@ -57,7 +55,7 @@ class MergeRequest < ActiveRecord::Base transition [:reopened, :opened] => :closed end - event :merge do + event :mark_as_merged do transition [:reopened, :opened, :locked] => :merged end @@ -205,7 +203,10 @@ class MergeRequest < ActiveRecord::Base end def check_if_can_be_merged - if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged? + can_be_merged = + project.repository.can_be_merged?(source_sha, target_branch) + + if can_be_merged mark_as_mergeable else mark_as_unmergeable @@ -220,18 +221,6 @@ class MergeRequest < ActiveRecord::Base self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last end - def automerge!(current_user, commit_message = nil) - return unless automergeable? - - MergeRequests::AutoMergeService. - new(target_project, current_user). - execute(self, commit_message) - end - - def remove_source_branch? - self.should_remove_source_branch && !self.source_project.root_ref?(self.source_branch) && !self.for_fork? - end - def open? opened? || reopened? end @@ -240,11 +229,11 @@ class MergeRequest < ActiveRecord::Base title =~ /\A\[?WIP\]?:? /i end - def automergeable? + def mergeable? open? && !work_in_progress? && can_be_merged? end - def automerge_status + def gitlab_merge_status if work_in_progress? "work_in_progress" else @@ -271,14 +260,14 @@ class MergeRequest < ActiveRecord::Base # # see "git diff" def to_diff(current_user) - Gitlab::Satellite::MergeAction.new(current_user, self).diff_in_satellite + target_project.repository.diff_text(target_branch, source_sha) end # Returns the commit as a series of email patches. # # see "git format-patch" def to_patch(current_user) - Gitlab::Satellite::MergeAction.new(current_user, self).format_patch + target_project.repository.format_patch(target_branch, source_sha) end def hook_attrs @@ -429,4 +418,30 @@ class MergeRequest < ActiveRecord::Base "Open" end end + + def target_sha + @target_sha ||= target_project. + repository.commit(target_branch).sha + end + + def source_sha + commits.first.sha + end + + def fetch_ref + target_project.repository.fetch_ref( + source_project.repository.path_to_repo, + "refs/heads/#{source_branch}", + "refs/merge-requests/#{iid}/head" + ) + end + + def in_locked_state + begin + lock_mr + yield + ensure + unlock_mr if locked? + end + end end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index df1c2b78758..e317c8eac4d 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -16,9 +16,8 @@ require Rails.root.join("app/models/commit") class MergeRequestDiff < ActiveRecord::Base include Sortable - # Prevent store of diff - # if commits amount more then 200 - COMMITS_SAFE_SIZE = 200 + # Prevent store of diff if commits amount more then 500 + COMMITS_SAFE_SIZE = 500 attr_reader :commits, :diffs @@ -124,12 +123,12 @@ class MergeRequestDiff < ActiveRecord::Base if new_diffs.any? if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES self.state = :overflow_diff_files_limit - new_diffs = [] + new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES] end if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES self.state = :overflow_diff_lines_limit - new_diffs = [] + new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES] end end @@ -160,12 +159,21 @@ class MergeRequestDiff < ActiveRecord::Base private def compare_result - @compare_result ||= CompareService.new.execute( - merge_request.author, - merge_request.source_project, - merge_request.source_branch, - merge_request.target_project, - merge_request.target_branch, - ) + @compare_result ||= + begin + # Update ref for merge request + merge_request.fetch_ref + + # Get latest sha of branch from source project + source_sha = merge_request.source_project.commit(source_branch).sha + + Gitlab::CompareResult.new( + Gitlab::Git::Compare.new( + merge_request.target_project.repository.raw_repository, + merge_request.target_branch, + source_sha, + ) + ) + end end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 30ffacadded..161a16ca61c 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -118,12 +118,11 @@ class Namespace < ActiveRecord::Base gitlab_shell.add_namespace(path_was) if gitlab_shell.mv_namespace(path_was, path) - # If repositories moved successfully we need to remove old satellites - # and send update instructions to users. + # If repositories moved successfully we need to + # send update instructions to users. # However we cannot allow rollback since we moved namespace dir # So we basically we mute exceptions in next actions begin - gitlab_shell.rm_satellites(path_was) send_update_instructions rescue # Returning false does not rollback after_* transaction but gives diff --git a/app/models/project.rb b/app/models/project.rb index 3dc1729e812..69f9af91c51 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -215,7 +215,7 @@ class Project < ActiveRecord::Base end def search(query) - joins(:namespace).where('projects.archived = ?', false). + joins(:namespace). where('LOWER(projects.name) LIKE :query OR LOWER(projects.path) LIKE :query OR LOWER(namespaces.name) LIKE :query OR @@ -520,14 +520,6 @@ class Project < ActiveRecord::Base !repository.exists? || repository.empty? end - def ensure_satellite_exists - self.satellite.create unless self.satellite.exists? - end - - def satellite - @satellite ||= Gitlab::Satellite::Satellite.new(self) - end - def repo repository.raw end @@ -597,14 +589,11 @@ class Project < ActiveRecord::Base new_path_with_namespace = File.join(namespace_dir, path) if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace) - # If repository moved successfully we need to remove old satellite - # and send update instructions to users. + # If repository moved successfully we need to send update instructions to users. # However we cannot allow rollback since we moved repository # So we basically we mute exceptions in next actions begin gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") - gitlab_shell.rm_satellites(old_path_with_namespace) - ensure_satellite_exists send_move_instructions reset_events_cache rescue @@ -702,7 +691,6 @@ class Project < ActiveRecord::Base def create_repository if forked? if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path) - ensure_satellite_exists true else errors.add(:base, 'Failed to fork repository via gitlab-shell') diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 5aaa4e85cbc..ecdcd48ae60 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -74,6 +74,8 @@ class GitlabCiService < CiService else :error end + rescue Errno::ECONNREFUSED + :error end def fork_registration(new_project, private_token) @@ -103,6 +105,8 @@ class GitlabCiService < CiService if response.code == 200 and response["coverage"] response["coverage"] end + rescue Errno::ECONNREFUSED + nil end def build_page(sha, ref) diff --git a/app/models/repository.rb b/app/models/repository.rb index 24c32d90051..79b48ebfedf 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,4 +1,9 @@ +require 'securerandom' + class Repository + class PreReceiveError < StandardError; end + class CommitError < StandardError; end + include Gitlab::ShellAdapter attr_accessor :raw_repository, :path_with_namespace, :project @@ -368,6 +373,89 @@ class Repository @root_ref ||= raw_repository.root_ref end + def commit_file(user, path, content, message, branch) + commit_with_hooks(user, branch) do |ref| + path[0] = '' if path[0] == '/' + + committer = user_to_comitter(user) + options = {} + options[:committer] = committer + options[:author] = committer + options[:commit] = { + message: message, + branch: ref, + } + + options[:file] = { + content: content, + path: path + } + + Gitlab::Git::Blob.commit(raw_repository, options) + end + end + + def remove_file(user, path, message, branch) + commit_with_hooks(user, branch) do |ref| + path[0] = '' if path[0] == '/' + + committer = user_to_comitter(user) + options = {} + options[:committer] = committer + options[:author] = committer + options[:commit] = { + message: message, + branch: ref + } + + options[:file] = { + path: path + } + + Gitlab::Git::Blob.remove(raw_repository, options) + end + end + + def user_to_comitter(user) + { + email: user.email, + name: user.name, + time: Time.now + } + end + + def can_be_merged?(source_sha, target_branch) + our_commit = rugged.branches[target_branch].target + their_commit = rugged.lookup(source_sha) + + if our_commit && their_commit + !rugged.merge_commits(our_commit, their_commit).conflicts? + else + false + end + end + + def merge(user, source_sha, target_branch, options = {}) + our_commit = rugged.branches[target_branch].target + their_commit = rugged.lookup(source_sha) + + raise "Invalid merge target" if our_commit.nil? + raise "Invalid merge source" if their_commit.nil? + + merge_index = rugged.merge_commits(our_commit, their_commit) + return false if merge_index.conflicts? + + commit_with_hooks(user, target_branch) do |ref| + actual_options = options.merge( + parents: [our_commit, their_commit], + tree: merge_index.write_tree(rugged), + update_ref: ref + ) + + Rugged::Commit.create(rugged, actual_options) + end + end + def merged_to_root_ref?(branch_name) branch_commit = commit(branch_name) root_ref_commit = commit(root_ref) @@ -412,6 +500,64 @@ class Repository ) end + def fetch_ref(source_path, source_ref, target_ref) + args = %W(git fetch #{source_path} #{source_ref}:#{target_ref}) + Gitlab::Popen.popen(args, path_to_repo) + end + + def commit_with_hooks(current_user, branch) + oldrev = Gitlab::Git::BLANK_SHA + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch + gl_id = Gitlab::ShellEnv.gl_id(current_user) + was_empty = empty? + + # Create temporary ref + random_string = SecureRandom.hex + tmp_ref = "refs/tmp/#{random_string}/head" + + unless was_empty + oldrev = find_branch(branch).target + rugged.references.create(tmp_ref, oldrev) + end + + # Make commit in tmp ref + newrev = yield(tmp_ref) + + unless newrev + raise CommitError.new('Failed to create commit') + end + + # Run GitLab pre-receive hook + pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo) + status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) + + if status + if was_empty + # Create branch + rugged.references.create(ref, newrev) + else + # Update head + current_head = find_branch(branch).target + + # Make sure target branch was not changed during pre-receive hook + if current_head == oldrev + rugged.references.update(ref, newrev) + else + raise CommitError.new('Commit was rejected because branch received new push') + end + end + + # Run GitLab post receive hook + post_receive_hook = Gitlab::Git::Hook.new('post-receive', path_to_repo) + status = post_receive_hook.trigger(gl_id, oldrev, newrev, ref) + else + # Remove tmp ref and return error to user + rugged.references.delete(tmp_ref) + + raise PreReceiveError.new('Commit was rejected by pre-receive hook') + end + end + private def cache diff --git a/app/services/base_service.rb b/app/services/base_service.rb index 6d9ed345914..f00ec7408b6 100644 --- a/app/services/base_service.rb +++ b/app/services/base_service.rb @@ -31,6 +31,10 @@ class BaseService SystemHooksService.new end + def repository + project.repository + end + # Add an error to the specified model for restricted visibility levels def deny_visibility_level(model, denied_visibility_level = nil) denied_visibility_level ||= model.visibility_level diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb index 6aa9df4b194..70f642baaaa 100644 --- a/app/services/compare_service.rb +++ b/app/services/compare_service.rb @@ -1,27 +1,28 @@ +require 'securerandom' + # Compare 2 branches for one repo or between repositories # and return Gitlab::CompareResult object that responds to commits and diffs class CompareService - def execute(current_user, source_project, source_branch, target_project, target_branch) - # Try to compare branches to get commits list and diffs - # - # Note: Use satellite only when need to compare between two repos - # because satellites are slower than operations on bare repo - if target_project == source_project - Gitlab::CompareResult.new( - Gitlab::Git::Compare.new( - target_project.repository.raw_repository, - target_branch, - source_branch, - ) + def execute(source_project, source_branch, target_project, target_branch) + source_sha = source_project.commit(source_branch).sha + + # If compare with other project we need to fetch ref first + unless target_project == source_project + random_string = SecureRandom.hex + + target_project.repository.fetch_ref( + source_project.repository.path_to_repo, + "refs/heads/#{source_branch}", + "refs/tmp/#{random_string}/head" ) - else - Gitlab::Satellite::CompareAction.new( - current_user, - target_project, - target_branch, - source_project, - source_branch - ).result end + + Gitlab::CompareResult.new( + Gitlab::Git::Compare.new( + target_project.repository.raw_repository, + target_branch, + source_sha, + ) + ) end end diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index bd245100955..7aecee217d8 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -1,17 +1,75 @@ module Files class BaseService < ::BaseService - attr_reader :ref, :path + class ValidationError < StandardError; end - def initialize(project, user, params, ref, path = nil) - @project, @current_user, @params = project, user, params.dup - @ref = ref - @path = path + def execute + @current_branch = params[:current_branch] + @target_branch = params[:target_branch] + @commit_message = params[:commit_message] + @file_path = params[:file_path] + @file_content = if params[:file_content_encoding] == 'base64' + Base64.decode64(params[:file_content]) + else + params[:file_content] + end + + # Validate parameters + validate + + # Create new branch if it different from current_branch + if @target_branch != @current_branch + create_target_branch + end + + if sha = commit + success + else + error("Something went wrong. Your changes were not committed") + end + rescue Repository::CommitError, Repository::PreReceiveError, ValidationError => ex + error(ex.message) end private - def repository - project.repository + def current_branch + @current_branch ||= params[:current_branch] + end + + def target_branch + @target_branch ||= params[:target_branch] + end + + def raise_error(message) + raise ValidationError.new(message) + end + + def validate + allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch) + + unless allowed + raise_error("You are not allowed to push into this branch") + end + + unless project.empty_repo? + unless repository.branch_names.include?(@current_branch) + raise_error("You can only create files if you are on top of a branch") + end + + if @current_branch != @target_branch + if repository.branch_names.include?(@target_branch) + raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes") + end + end + end + end + + def create_target_branch + result = CreateBranchService.new(project, current_user).execute(@target_branch, @current_branch) + + unless result[:status] == :success + raise_error("Something went wrong when we tried to create #{@target_branch} for you") + end end end end diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index 23833aa78ec..91d715b2d63 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -1,52 +1,30 @@ require_relative "base_service" module Files - class CreateService < BaseService - def execute - allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) + class CreateService < Files::BaseService + def commit + repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch) + end - unless allowed - return error("You are not allowed to create file in this branch") - end + def validate + super - file_name = File.basename(path) - file_path = path + file_name = File.basename(@file_path) unless file_name =~ Gitlab::Regex.file_name_regex - return error( + raise_error( 'Your changes could not be committed, because the file name ' + Gitlab::Regex.file_name_regex_message ) end - if project.empty_repo? - # everything is ok because repo does not have a commits yet - else - unless repository.branch_names.include?(ref) - return error("You can only create files if you are on top of a branch") - end - - blob = repository.blob_at_branch(ref, file_path) + unless project.empty_repo? + blob = repository.blob_at_branch(@current_branch, @file_path) if blob - return error("Your changes could not be committed, because file with such name exists") + raise_error("Your changes could not be committed, because file with such name exists") end end - - - new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path) - created_successfully = new_file_action.commit!( - params[:content], - params[:commit_message], - params[:encoding], - params[:new_branch] - ) - - if created_successfully - success - else - error("Your changes could not be committed, because the file has been changed") - end end end end diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb index 1497a0f883b..27c881c3430 100644 --- a/app/services/files/delete_service.rb +++ b/app/services/files/delete_service.rb @@ -1,36 +1,9 @@ require_relative "base_service" module Files - class DeleteService < BaseService - def execute - allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) - - unless allowed - return error("You are not allowed to push into this branch") - end - - unless repository.branch_names.include?(ref) - return error("You can only create files if you are on top of a branch") - end - - blob = repository.blob_at_branch(ref, path) - - unless blob - return error("You can only edit text files") - end - - delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path) - - deleted_successfully = delete_file_action.commit!( - nil, - params[:commit_message] - ) - - if deleted_successfully - success - else - error("Your changes could not be committed, because the file has been changed") - end + class DeleteService < Files::BaseService + def commit + repository.remove_file(current_user, @file_path, @commit_message, @target_branch) end end end diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index 0724d3ae634..a20903c6f02 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -1,39 +1,9 @@ require_relative "base_service" module Files - class UpdateService < BaseService - def execute - allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) - - unless allowed - return error("You are not allowed to push into this branch") - end - - unless repository.branch_names.include?(ref) - return error("You can only create files if you are on top of a branch") - end - - blob = repository.blob_at_branch(ref, path) - - unless blob - return error("You can only edit text files") - end - - edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path) - edit_file_action.commit!( - params[:content], - params[:commit_message], - params[:encoding], - params[:new_branch] - ) - - success - rescue Gitlab::Satellite::CheckoutFailed => ex - error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400) - rescue Gitlab::Satellite::CommitFailed => ex - error("Your changes could not be committed. Maybe there was nothing to commit?", 409) - rescue Gitlab::Satellite::PushFailed => ex - error("Your changes could not be committed. Maybe the file was changed by another process?", 409) + class UpdateService < Files::BaseService + def commit + repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch) end end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 5a2c97b08af..81535450ac1 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -10,16 +10,14 @@ class GitPushService # # Next, this method: # 1. Creates the push event - # 2. Ensures that the project satellite exists - # 3. Updates merge requests - # 4. Recognizes cross-references from commit messages - # 5. Executes the project's web hooks - # 6. Executes the project's services + # 2. Updates merge requests + # 3. Recognizes cross-references from commit messages + # 4. Executes the project's web hooks + # 5. Executes the project's services # def execute(project, user, oldrev, newrev, ref) @project, @user = project, user - project.ensure_satellite_exists project.repository.expire_cache if push_remove_branch?(ref, newrev) @@ -133,7 +131,8 @@ class GitPushService end def is_default_branch?(ref) - Gitlab::Git.branch_ref?(ref) && Gitlab::Git.ref_name(ref) == project.default_branch + Gitlab::Git.branch_ref?(ref) && + (Gitlab::Git.ref_name(ref) == project.default_branch || project.default_branch.nil?) end def commit_user(commit) diff --git a/app/services/merge_requests/auto_merge_service.rb b/app/services/merge_requests/auto_merge_service.rb deleted file mode 100644 index cdedf48b0c0..00000000000 --- a/app/services/merge_requests/auto_merge_service.rb +++ /dev/null @@ -1,30 +0,0 @@ -module MergeRequests - # AutoMergeService class - # - # Do git merge in satellite and in case of success - # mark merge request as merged and execute all hooks and notifications - # Called when you do merge via GitLab UI - class AutoMergeService < BaseMergeService - def execute(merge_request, commit_message) - merge_request.lock_mr - - if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message) - merge_request.merge - - create_merge_event(merge_request, current_user) - create_note(merge_request) - notification_service.merge_mr(merge_request, current_user) - execute_hooks(merge_request, 'merge') - - true - else - merge_request.unlock_mr - false - end - rescue - merge_request.unlock_mr if merge_request.locked? - merge_request.mark_as_unmergeable - false - end - end -end diff --git a/app/services/merge_requests/base_merge_service.rb b/app/services/merge_requests/base_merge_service.rb deleted file mode 100644 index 9579573adf9..00000000000 --- a/app/services/merge_requests/base_merge_service.rb +++ /dev/null @@ -1,10 +0,0 @@ -module MergeRequests - class BaseMergeService < MergeRequests::BaseService - - private - - def create_merge_event(merge_request, current_user) - EventCreateService.new.merge_mr(merge_request, current_user) - end - end -end diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 956480938c3..a9b29f9654d 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -12,12 +12,16 @@ module MergeRequests merge_request.target_project ||= (project.forked_from_project || project) merge_request.target_branch ||= merge_request.target_project.default_branch - unless merge_request.target_branch && merge_request.source_branch - return build_failed(merge_request, nil) + if merge_request.target_branch.blank? || merge_request.source_branch.blank? + message = + if params[:source_branch] || params[:target_branch] + "You must select source and target branch" + end + + return build_failed(merge_request, message) end compare_result = CompareService.new.execute( - current_user, merge_request.source_project, merge_request.source_branch, merge_request.target_project, @@ -40,7 +44,6 @@ module MergeRequests merge_request.compare_diffs = diffs elsif diffs == false - # satellite timeout return false merge_request.can_be_created = false merge_request.compare_failed = true end @@ -59,9 +62,6 @@ module MergeRequests end merge_request - - rescue Gitlab::Satellite::BranchesWithoutParent - return build_failed(merge_request, "Selected branches have no common commit so they cannot be merged.") end def build_failed(merge_request, message) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 327ead4ff3f..98a67c0bc99 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -1,22 +1,47 @@ module MergeRequests # MergeService class # - # Mark existing merge request as merged - # and execute all hooks and notifications - # Called when you do merge via command line and push code - # to target branch - class MergeService < BaseMergeService + # Do git merge and in case of success + # mark merge request as merged and execute all hooks and notifications + # Executed when you do merge via GitLab UI + # + class MergeService < MergeRequests::BaseService + attr_reader :merge_request, :commit_message + def execute(merge_request, commit_message) - merge_request.merge + @commit_message = commit_message + @merge_request = merge_request + + unless @merge_request.mergeable? + return error('Merge request is not mergeable') + end + + merge_request.in_locked_state do + if commit + after_merge + success + else + error('Can not merge changes') + end + end + end - create_merge_event(merge_request, current_user) - create_note(merge_request) - notification_service.merge_mr(merge_request, current_user) - execute_hooks(merge_request, 'merge') + private + + def commit + committer = repository.user_to_comitter(current_user) + + options = { + message: commit_message, + author: committer, + committer: committer + } + + repository.merge(current_user, merge_request.source_sha, merge_request.target_branch, options) + end - true - rescue - false + def after_merge + MergeRequests::PostMergeService.new(project, current_user).execute(merge_request) end end end diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb new file mode 100644 index 00000000000..aceb8cb9021 --- /dev/null +++ b/app/services/merge_requests/post_merge_service.rb @@ -0,0 +1,22 @@ +module MergeRequests + # PostMergeService class + # + # Mark existing merge request as merged + # and execute all hooks and notifications + # + class PostMergeService < MergeRequests::BaseService + def execute(merge_request) + merge_request.mark_as_merged + create_merge_event(merge_request, current_user) + create_note(merge_request) + notification_service.merge_mr(merge_request, current_user) + execute_hooks(merge_request, 'merge') + end + + private + + def create_merge_event(merge_request, current_user) + EventCreateService.new.merge_mr(merge_request, current_user) + end + end +end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index d0648da049b..e903e48e3cd 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -33,9 +33,9 @@ module MergeRequests merge_requests.uniq.select(&:source_project).each do |merge_request| - MergeRequests::MergeService. + MergeRequests::PostMergeService. new(merge_request.target_project, @current_user). - execute(merge_request, nil) + execute(merge_request) end end diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 403f419ec50..28872c89259 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -27,7 +27,6 @@ module Projects end end - project.satellite.destroy log_info("Project \"#{project.name}\" was removed") system_hook_service.execute_hooks_for(project, :destroy) true diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index f43c0ef70e9..550ed6897dd 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -33,9 +33,6 @@ module Projects raise TransferError.new("Project with same path in target namespace already exists") end - # Remove old satellite - project.satellite.destroy - # Apply new namespace id project.namespace = new_namespace project.save! @@ -51,9 +48,6 @@ module Projects # Move wiki repo also if present gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") - # Create a new satellite (reload project from DB) - Project.find(project.id).ensure_satellite_exists - # clear project cached events project.reset_events_cache diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index b67d2116fa4..330b8bbf9d2 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -28,6 +28,20 @@ = level %span.help-block#restricted-visibility-help Selected levels cannot be used by non-admin users for projects or snippets .form-group + = f.label :import_sources, class: 'control-label col-sm-2' + .col-sm-10 + - data_attrs = { toggle: 'buttons' } + .btn-group{ data: data_attrs } + - import_sources_checkboxes('import-sources-help').each do |source| + = source + %span.help-block#import-sources-help + Enabled sources for code import during project creation. OmniAuth must be configured for GitHub + = link_to "(?)", help_page_path("integration", "github") + , Bitbucket + = link_to "(?)", help_page_path("integration", "bitbucket") + and GitLab.com + = link_to "(?)", help_page_path("integration", "gitlab") + .form-group .col-sm-offset-2.col-sm-10 .checkbox = f.label :version_check_enabled do diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index f43d46356fa..d9b481404f7 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -23,6 +23,10 @@ = label_tag :abandoned do = check_box_tag :abandoned, 1, params[:abandoned] %span No activity over 6 month + .checkbox + = label_tag :with_archived do + = check_box_tag :with_archived, 1, params[:with_archived] + %span Show archived projects %fieldset %strong Visibility level: @@ -73,6 +77,8 @@ = visibility_level_icon(project.visibility_level) = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project] .pull-right + - if project.archived + %span.label.label-warning archived %span.label.label-gray = repository_size(project) = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml new file mode 100644 index 00000000000..8a397a84e0e --- /dev/null +++ b/app/views/dashboard/_groups_head.html.haml @@ -0,0 +1,7 @@ +%ul.center-top-menu + = nav_link(page: [dashboard_groups_path]) do + = link_to dashboard_groups_path, title: 'Your groups', data: {placement: 'right'} do + Your Groups + = nav_link(page: [explore_groups_path]) do + = link_to explore_groups_path, title: 'Explore groups', data: {toggle: 'tooltip', placement: 'bottom'} do + Explore Groups diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml new file mode 100644 index 00000000000..f7be194c696 --- /dev/null +++ b/app/views/dashboard/_projects_head.html.haml @@ -0,0 +1,10 @@ +%ul.center-top-menu + = nav_link(path: ['dashboard#show', 'root#show']) do + = link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + Your Projects + = nav_link(page: starred_dashboard_projects_path) do + = link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do + Starred Projects + = nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do + = link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do + Explore Projects diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index 0a354373b9b..0860fe3c761 100644 --- a/app/views/dashboard/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -1,14 +1,13 @@ - page_title "Groups" -%h3.page-title - Group Membership += render 'dashboard/groups_head' + +.slead + Group members have access to all group projects. - if current_user.can_create_group? %span.pull-right.hidden-xs - = link_to new_group_path, class: "btn btn-new" do + = link_to new_group_path, class: "btn btn-new btn-sm" do %i.fa.fa-plus New Group -%p.light - Group members have access to all group projects. -%hr .panel.panel-default .panel-heading %strong Groups diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index 8aaa0a7f071..98b8cde4766 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -1,4 +1,6 @@ - page_title "Starred Projects" += render 'dashboard/projects_head' + - if @projects.any? = render 'shared/show_aside' diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml index 5001c2101e1..a3a32b6932f 100644 --- a/app/views/dashboard/show.html.haml +++ b/app/views/dashboard/show.html.haml @@ -2,6 +2,8 @@ - if current_user = auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity") += render 'dashboard/projects_head' + - if @projects.any? = render 'shared/show_aside' diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index f3f0b778539..7dcefd330a1 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -1,5 +1,7 @@ - page_title "Groups" -.clearfix +- if current_user + = render 'dashboard/groups_head' +.clearfix.append-bottom-10 .pull-left = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f| = hidden_field_tag :sort, @sort @@ -28,15 +30,12 @@ = link_to explore_groups_path(sort: sort_value_oldest_updated) do = sort_title_oldest_updated -%hr - %ul.bordered-list - @groups.each do |group| %li .clearfix %h4 = link_to group_path(id: group.path) do - %i.fa.fa-users = group.name .clearfix %p diff --git a/app/views/explore/projects/_dropdown.html.haml b/app/views/explore/projects/_dropdown.html.haml new file mode 100644 index 00000000000..b23a3c1e5c1 --- /dev/null +++ b/app/views/explore/projects/_dropdown.html.haml @@ -0,0 +1,27 @@ +.dropdown.inline + %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} + %span.light sort: + - if @sort.present? + = sort_options_hash[@sort] + - elsif current_page?(trending_explore_projects_path) || current_page?(explore_root_path) + Trending projects + - elsif current_page?(starred_explore_projects_path) + Most stars + - else + = sort_title_recently_created + %b.caret + %ul.dropdown-menu + %li + = link_to trending_explore_projects_path do + Trending projects + = link_to starred_explore_projects_path do + Most stars + = link_to explore_projects_filter_path(sort: sort_value_recently_created) do + = sort_title_recently_created + = link_to explore_projects_filter_path(sort: sort_value_oldest_created) do + = sort_title_oldest_created + = link_to explore_projects_filter_path(sort: sort_value_recently_updated) do + = sort_title_recently_updated + = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do + = sort_title_oldest_updated + diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index 82622a58ed2..4b91291caf4 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -46,22 +46,4 @@ = link_to explore_projects_filter_path(tag: tag.name) do %i.fa.fa-tag = tag.name - - .dropdown.inline - %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} - %span.light sort: - - if @sort.present? - = sort_options_hash[@sort] - - else - = sort_title_recently_created - %b.caret - %ul.dropdown-menu - %li - = link_to explore_projects_filter_path(sort: sort_value_recently_created) do - = sort_title_recently_created - = link_to explore_projects_filter_path(sort: sort_value_oldest_created) do - = sort_title_oldest_created - = link_to explore_projects_filter_path(sort: sort_value_recently_updated) do - = sort_title_recently_updated - = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do - = sort_title_oldest_updated + = render 'explore/projects/dropdown' diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index ba2276f51ce..4956081e1ed 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,8 +1,9 @@ - page_title "Projects" +- if current_user + = render 'dashboard/projects_head' .clearfix = render 'filter' - -%hr +%br .public-projects %ul.bordered-list.top-list = render @projects diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index b5d146b1f2f..fdccbe5692f 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -1,9 +1,12 @@ - page_title "Starred Projects" +- if current_user + = render 'dashboard/projects_head' .explore-trending-block - %p.lead + .lead %i.fa.fa-star See most starred projects - %hr + .pull-right + = render 'explore/projects/dropdown' .public-projects %ul.bordered-list = render @starred_projects diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index 5e24df76a63..98a4174b426 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -1,4 +1,6 @@ - page_title "Trending Projects" +- if current_user + = render 'dashboard/projects_head' .explore-title %h3 Explore GitLab @@ -6,10 +8,11 @@ Discover projects and groups. Share your projects with others %hr .explore-trending-block - %p.lead + .lead %i.fa.fa-comments-o See most discussed projects for last month - %hr + .pull-right + = render 'explore/projects/dropdown' .public-projects %ul.bordered-list = render @trending_projects diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 6b7efa83dea..d06cfa7ff9f 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -12,11 +12,14 @@ - @projects.each do |project| %li .list-item-name - = visibility_level_icon(project.visibility_level) + %span{ class: visibility_level_color(project.visibility_level) } + = visibility_level_icon(project.visibility_level) %strong= link_to project.name_with_namespace, project + .pull-right + - if project.archived + %span.label.label-warning archived %span.label.label-gray = repository_size(project) - .pull-right = link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove" diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml index 56bb92a536e..17fee9c510d 100644 --- a/app/views/layouts/explore.html.haml +++ b/app/views/layouts/explore.html.haml @@ -1,5 +1,8 @@ - page_title "Explore" -- header_title "Explore GitLab", explore_root_path -- sidebar "explore" +- if current_user + - header_title "Dashboard", root_path +- else + - header_title "Explore GitLab", explore_root_path +- sidebar "dashboard" = render template: "layouts/application" diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index b3cd7b0e37b..12ddbe6f1b7 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -17,13 +17,13 @@ %li.visible-sm.visible-xs = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('search') - %li.hidden-xs + -#%li.hidden-xs = link_to help_path, title: 'Help', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('question-circle fw') - %li + -#%li = link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('globe fw') - %li + -#%li = link_to user_snippets_path(current_user), title: 'Your snippets', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('clipboard fw') - if current_user.is_admin? @@ -34,7 +34,7 @@ %li.hidden-xs = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('plus fw') - %li + -#%li = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('cog fw') %li diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 687c1fc3dd2..8f010196d1a 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,36 +1,43 @@ %ul.nav.nav-sidebar - = nav_link(path: ['dashboard#show', 'root#show'], html_options: {class: 'home'}) do - = link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do + = link_to (current_user ? root_path : explore_root_path), title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do = icon('dashboard fw') %span - Your Projects - = nav_link(path: 'projects#starred') do - = link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do - = icon('star fw') - %span - Starred Projects + Projects = nav_link(controller: :groups) do - = link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do + = link_to (current_user ? dashboard_groups_path : explore_groups_path), title: 'Groups', data: {placement: 'right'} do = icon('group fw') %span Groups - = nav_link(controller: :milestones) do - = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do - = icon('clock-o fw') - %span - Milestones - = nav_link(path: 'dashboard#issues') do - = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do - = icon('exclamation-circle fw') - %span - Issues - %span.count= current_user.assigned_issues.opened.count - = nav_link(path: 'dashboard#merge_requests') do - = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do - = icon('tasks fw') + - if current_user + = nav_link(controller: :milestones) do + = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do + = icon('clock-o fw') + %span + Milestones + = nav_link(path: 'dashboard#issues') do + = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do + = icon('exclamation-circle fw') + %span + Issues + %span.count= current_user.assigned_issues.opened.count + = nav_link(path: 'dashboard#merge_requests') do + = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do + = icon('tasks fw') + %span + Merge Requests + %span.count= current_user.assigned_merge_requests.opened.count + = nav_link(controller: :snippets) do + = link_to (current_user ? user_snippets_path(current_user) : snippets_path), title: 'Your snippets', data: {placement: 'right'} do + = icon('dashboard fw') %span - Merge Requests - %span.count= current_user.assigned_merge_requests.opened.count + Snippets + - if current_user + = nav_link(controller: :profile) do + = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do + = icon('user fw') + %span + Profile = nav_link(controller: :help) do = link_to help_path, title: 'Help', data: {placement: 'right'} do = icon('question-circle fw') diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml deleted file mode 100644 index 66870e84ceb..00000000000 --- a/app/views/layouts/nav/_explore.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -%ul.nav.nav-sidebar - = nav_link(path: 'projects#trending') do - = link_to explore_root_path, title: 'Trending Projects', data: {placement: 'right'} do - = icon('comments fw') - %span Trending Projects - = nav_link(path: 'projects#starred') do - = link_to starred_explore_projects_path, title: 'Most-starred Projects', data: {placement: 'right'} do - = icon('star fw') - %span Most-starred Projects - = nav_link(path: 'projects#index') do - = link_to explore_projects_path, title: 'All Projects', data: {placement: 'right'} do - = icon('bookmark fw') - %span All Projects - = nav_link(controller: :groups) do - = link_to explore_groups_path, title: 'All Groups', data: {placement: 'right'} do - = icon('group fw') - %span All Groups - diff --git a/app/views/layouts/nav/_snippets.html.haml b/app/views/layouts/nav/_snippets.html.haml deleted file mode 100644 index 458b76a2c99..00000000000 --- a/app/views/layouts/nav/_snippets.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -%ul.nav.nav-sidebar - - if current_user - = nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do - = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do - = icon('dashboard fw') - %span - Your Snippets - = nav_link(path: snippets_path) do - = link_to snippets_path, title: 'Discover snippets', data: {placement: 'right'} do - = icon('globe fw') - %span - Discover Snippets diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index 9b0f40073ab..b77fe09fc2a 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -1,5 +1,8 @@ - page_title 'Snippets' -- header_title 'Snippets', snippets_path -- sidebar "snippets" +- if current_user + - header_title "Dashboard", root_path +- else + - header_title 'Snippets', snippets_path +- sidebar "dashboard" = render template: "layouts/application" diff --git a/app/views/profiles/two_factor_auths/_codes.html.haml b/app/views/profiles/two_factor_auths/_codes.html.haml index 1b1395eaa17..43b58be7f98 100644 --- a/app/views/profiles/two_factor_auths/_codes.html.haml +++ b/app/views/profiles/two_factor_auths/_codes.html.haml @@ -1,6 +1,8 @@ %p.slead Should you ever lose your phone, each of these recovery codes can be used one - time each to regain access to your account. Please save them in a safe place. + time each to regain access to your account. Please save them in a safe place, or you + %b will + lose access to your account. .codes.well %ul diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml index 745163e79a7..2987f6b5b22 100644 --- a/app/views/projects/_bitbucket_import_modal.html.haml +++ b/app/views/projects/_bitbucket_import_modal.html.haml @@ -5,9 +5,9 @@ %a.close{href: "#", "data-dismiss" => "modal"} × %h3 Import projects from Bitbucket .modal-body - To enable importing projects from Bitbucket, + To enable importing projects from Bitbucket, - if current_user.admin? - you need to + as administrator you need to configure - else - your GitLab administrator needs to - == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/bitbucket.md'}. + ask your GitLab administrator to configure + == #{link_to 'OAuth integration', help_page_path("integration", "bitbucket")}. diff --git a/app/views/projects/_github_import_modal.html.haml b/app/views/projects/_github_import_modal.html.haml index de58b27df23..46ad1559356 100644 --- a/app/views/projects/_github_import_modal.html.haml +++ b/app/views/projects/_github_import_modal.html.haml @@ -5,9 +5,9 @@ %a.close{href: "#", "data-dismiss" => "modal"} × %h3 Import projects from GitHub .modal-body - To enable importing projects from GitHub, + To enable importing projects from GitHub, - if current_user.admin? - you need to + as administrator you need to configure - else - your GitLab administrator needs to - == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/github.md'}.
\ No newline at end of file + ask your Gitlab administrator to configure + == #{link_to 'OAuth integration', help_page_path("integration", "github")}. diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml index ae6c25f9371..377cf0187b8 100644 --- a/app/views/projects/_gitlab_import_modal.html.haml +++ b/app/views/projects/_gitlab_import_modal.html.haml @@ -5,9 +5,9 @@ %a.close{href: "#", "data-dismiss" => "modal"} × %h3 Import projects from GitLab.com .modal-body - To enable importing projects from GitLab.com, + To enable importing projects from GitLab.com, - if current_user.admin? - you need to + as administrator you need to configure - else - your GitLab administrator needs to - == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/gitlab.md'}.
\ No newline at end of file + ask your GitLab administrator to configure + == #{link_to 'OAuth integration', help_page_path("integration", "gitlab")}. diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 96f188e4aa7..9c3e1703c89 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -12,8 +12,8 @@ \/ = text_field_tag 'file_name', params[:file_name], placeholder: "File name", required: true, class: 'form-control new-file-name' - .pull-right - = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control' + .pull-right + = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control' .file-content.code %pre.js-edit-mode-pane#editor diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index dac984f8c31..7c2a4fece94 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -6,11 +6,12 @@ = render 'shared/commit_message_container', params: params, placeholder: 'Add new file' - .form-group.branch - = label_tag 'branch', class: 'control-label' do - Branch - .col-sm-10 - = text_field_tag 'new_branch', @ref, class: "form-control" + - unless @project.empty_repo? + .form-group.branch + = label_tag 'branch', class: 'control-label' do + Branch + .col-sm-10 + = text_field_tag 'new_branch', @ref, class: "form-control" = hidden_field_tag 'content', '', id: 'file-content' = render 'projects/commit_button', ref: @ref, diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index ff9c0cdb283..7709330611a 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -35,7 +35,7 @@ - if @merge_request.compare_failed .alert.alert-danger %h4 Compare failed - %p We can't compare selected branches. It may be because of huge diff or satellite timeout. Please try again or select different branches. + %p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches. - else .light-well .center diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 633a54f3620..76f44211dac 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -24,7 +24,7 @@ = icon('history') Commits %span.badge= @commits.size - %li.diffs-tab + %li.diffs-tab.active = link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do = icon('list-alt') Changes @@ -33,7 +33,7 @@ .tab-content #commits.commits.tab-pane = render "projects/commits/commits", project: @project - #diffs.diffs.tab-pane + #diffs.diffs.tab-pane.active - if @diffs.present? = render "projects/diffs/diffs", diffs: @diffs, project: @project - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE diff --git a/app/views/projects/merge_requests/automerge.js.haml b/app/views/projects/merge_requests/merge.js.haml index 33321651e32..33321651e32 100644 --- a/app/views/projects/merge_requests/automerge.js.haml +++ b/app/views/projects/merge_requests/merge.js.haml diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml index 3b7f283daf0..a71b181a6a5 100644 --- a/app/views/projects/merge_requests/show/_commits.html.haml +++ b/app/views/projects/merge_requests/show/_commits.html.haml @@ -1 +1 @@ -= render "projects/commits/commits", project: @merge_request.source_project += render "projects/commits/commits", project: @merge_request.project diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml index 786b5f39063..626970f39be 100644 --- a/app/views/projects/merge_requests/show/_diffs.html.haml +++ b/app/views/projects/merge_requests/show/_diffs.html.haml @@ -1,5 +1,5 @@ - if @merge_request_diff.collected? - = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project + = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.project - elsif @merge_request_diff.empty? .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} - else diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 8c61e819374..0aad9bb3e88 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -3,8 +3,6 @@ .mr-widget-body - if @project.archived? = render 'projects/merge_requests/widget/open/archived' - - elsif !@project.satellite.exists? - = render 'projects/merge_requests/widget/open/no_satellite' - elsif @merge_request.commits.blank? = render 'projects/merge_requests/widget/open/nothing' - elsif @merge_request.branch_missing? diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml index 263cab7a9e8..a489d4f9b24 100644 --- a/app/views/projects/merge_requests/widget/_show.html.haml +++ b/app/views/projects/merge_requests/widget/_show.html.haml @@ -11,10 +11,10 @@ var merge_request_widget; merge_request_widget = new MergeRequestWidget({ - url_to_automerge_check: "#{automerge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", + url_to_automerge_check: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", check_enable: #{@merge_request.unchecked? ? "true" : "false"}, url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", ci_enable: #{@project.ci_service ? "true" : "false"}, - current_status: "#{@merge_request.automerge_status}", + current_status: "#{@merge_request.gitlab_merge_status}", }); diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index df20205de1c..b61e193fc42 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -1,4 +1,4 @@ -= form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f| += form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f| = hidden_field_tag :authenticity_token, form_authenticity_token .accept-merge-holder.clearfix.js-toggle-container .accept-action diff --git a/app/views/projects/merge_requests/widget/open/_no_satellite.html.haml b/app/views/projects/merge_requests/widget/open/_no_satellite.html.haml deleted file mode 100644 index 3718cfd8333..00000000000 --- a/app/views/projects/merge_requests/widget/open/_no_satellite.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -%p - %span - %strong This repository does not have a satellite. Please ask an administrator to fix this issue! diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index d25fe68242b..636218368cc 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -22,70 +22,75 @@ .col-sm-10 = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} - %hr + - if import_sources_enabled? + %hr - .project-import.js-toggle-container - .form-group - %label.control-label Import project 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', "data-no-turbolink" => "true" 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 - - 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 new_import_google_code_path, class: 'btn' do - %i.fa.fa-google - Google Code - - = 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 + .project-import.js-toggle-container + .form-group + %label.control-label Import project from .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git' - .well.prepend-top-20 - %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/importing/migrating_from_svn.html"}. + - if github_import_enabled? + - if github_import_configured? + = link_to status_import_github_path, class: 'btn import_github' do + %i.fa.fa-github + GitHub + - else + = link_to '#', class: 'how_to_import_link light btn import_github' do + %i.fa.fa-github + GitHub + = render 'github_import_modal' + + - if bitbucket_import_enabled? + - if bitbucket_import_configured? + = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + - else + = link_to status_import_bitbucket_path, class: 'how_to_import_link light btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + = render 'bitbucket_import_modal' + + - if gitlab_import_enabled? + - if gitlab_import_configured? + = link_to status_import_gitlab_path, class: 'btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + - else + = link_to status_import_gitlab_path, class: 'how_to_import_link light btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + = render 'gitlab_import_modal' + + - if gitorious_import_enabled? + = link_to new_import_gitorious_path, class: 'btn import_gitorious' do + %i.icon-gitorious.icon-gitorious-small + Gitorious.org + + - if google_code_import_enabled? + = link_to new_import_google_code_path, class: 'btn import_google_code' do + %i.fa.fa-google + Google Code + + - if git_import_enabled? + = link_to "#", class: 'btn js-toggle-button import_git' 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' + .well.prepend-top-20 + %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/importing/migrating_from_svn.html"}. %hr.prepend-botton-10 diff --git a/app/views/snippets/_head.html.haml b/app/views/snippets/_head.html.haml new file mode 100644 index 00000000000..0adf6b91f2c --- /dev/null +++ b/app/views/snippets/_head.html.haml @@ -0,0 +1,7 @@ +%ul.center-top-menu + = nav_link(page: user_snippets_path(current_user), html_options: {class: 'home'}) do + = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do + Your Snippets + = nav_link(page: snippets_path) do + = link_to snippets_path, title: 'Explore snippets', data: {placement: 'right'} do + Explore Snippets diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml index 0718f743828..62a967a2e06 100644 --- a/app/views/snippets/current_user_index.html.haml +++ b/app/views/snippets/current_user_index.html.haml @@ -1,13 +1,13 @@ - page_title "Your Snippets" -%h3.page-title - Your Snippets - .pull-right - = link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do - Add new snippet += render 'head' -%p.light +.slead Share code pastes with others out of git repository + .pull-right + = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do + Add new snippet + %ul.nav.nav-tabs = nav_tab :scope, nil do = link_to user_snippets_path(@user) do diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml index e9bb6a908d3..8608815e0a6 100644 --- a/app/views/snippets/index.html.haml +++ b/app/views/snippets/index.html.haml @@ -1,17 +1,9 @@ - page_title "Public Snippets" -%h3.page-title - Public snippets +- if current_user + = render 'head' - .pull-right - - if current_user - = link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do - Add new snippet - = link_to user_snippets_path(current_user), class: "btn btn-grouped" do - Your snippets - -%p.light +.slead Public snippets created by you and other users are listed here -%hr = render 'snippets' diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 089e8122918..aed00f9caeb 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,5 +1,5 @@ - page_title @snippet.title, "Snippets" -%h3.page-title +%h4.page-title = @snippet.title - if @snippet.private? @@ -8,17 +8,14 @@ private .pull-right - = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do + = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do Add new snippet -%hr -.append-bottom-20 +.append-bottom-10.prepend-top-10 .pull-right - = "##{@snippet.id}" %span.light - by + created by = link_to user_snippets_path(@snippet.author) do - = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16", alt: '' = @snippet.author_name .back-link @@ -27,7 +24,7 @@ ← your snippets - else = link_to snippets_path do - ← discover snippets + ← explore snippets .file-holder .file-title diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml index 23700eb39da..7af5352da34 100644 --- a/app/views/snippets/user_index.html.haml +++ b/app/views/snippets/user_index.html.haml @@ -1,14 +1,13 @@ - page_title "Snippets", @user.name -%h3.page-title - = image_tag avatar_icon(@user.email), class: "avatar s24" - = @user.name - %span - \/ - Snippets - - if current_user - = link_to new_snippet_path, class: "btn btn-sm add_new pull-right", title: "New Snippet" do - Add new snippet -%hr +%ol.breadcrumb + %li + = link_to snippets_path do + Snippets + %li + = @user.name + .pull-right.hidden-xs + = link_to user_path(@user) do + #{@user.name} profile page = render 'snippets' diff --git a/app/workers/auto_merge_worker.rb b/app/workers/auto_merge_worker.rb deleted file mode 100644 index a6dd73eee5f..00000000000 --- a/app/workers/auto_merge_worker.rb +++ /dev/null @@ -1,13 +0,0 @@ -class AutoMergeWorker - include Sidekiq::Worker - - sidekiq_options queue: :default - - def perform(merge_request_id, current_user_id, params) - params = params.with_indifferent_access - current_user = User.find(current_user_id) - merge_request = MergeRequest.find(merge_request_id) - merge_request.should_remove_source_branch = params[:should_remove_source_branch] - merge_request.automerge!(current_user, params[:commit_message]) - end -end diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb new file mode 100644 index 00000000000..6a8665c179a --- /dev/null +++ b/app/workers/merge_worker.rb @@ -0,0 +1,19 @@ +class MergeWorker + include Sidekiq::Worker + + sidekiq_options queue: :default + + def perform(merge_request_id, current_user_id, params) + params = params.with_indifferent_access + current_user = User.find(current_user_id) + merge_request = MergeRequest.find(merge_request_id) + + result = MergeRequests::MergeService.new(merge_request.target_project, current_user). + execute(merge_request, params[:commit_message]) + + if result[:status] == :success && params[:should_remove_source_branch].present? + DeleteBranchService.new(merge_request.source_project, current_user). + execute(merge_request.source_branch) + end + end +end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 94832872d13..b546f8777e1 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -27,7 +27,6 @@ class RepositoryImportWorker project.import_finish project.save - project.satellite.create unless project.satellite.exists? ProjectCacheWorker.perform_async(project.id) Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 654de6238d0..bd26ac1da20 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -148,6 +148,7 @@ Settings.gitlab.default_projects_features['snippets'] = false if Settings. Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root) Settings.gitlab['restricted_signup_domains'] ||= [] +Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','git'] # # Reply by email diff --git a/config/routes.rb b/config/routes.rb index 1166a4b3eba..d7307a61ede 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -463,8 +463,8 @@ Gitlab::Application.routes.draw do member do get :diffs get :commits - post :automerge - get :automerge_check + post :merge + get :merge_check get :ci_status post :toggle_subscription end diff --git a/db/migrate/20150812080800_add_settings_import_sources.rb b/db/migrate/20150812080800_add_settings_import_sources.rb new file mode 100644 index 00000000000..276d2fdb2b1 --- /dev/null +++ b/db/migrate/20150812080800_add_settings_import_sources.rb @@ -0,0 +1,11 @@ +require 'yaml' + +class AddSettingsImportSources < ActiveRecord::Migration + def change + unless column_exists?(:application_settings, :import_sources) + add_column :application_settings, :import_sources, :text + import_sources = YAML::dump(Settings.gitlab['import_sources']) + execute("update application_settings set import_sources = '#{import_sources}'") + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 0827e810f18..2b9a3e7f011 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -44,6 +44,7 @@ ActiveRecord::Schema.define(version: 20150818213832) do t.boolean "user_oauth_applications", default: true t.string "after_sign_out_path" t.integer "session_expire_delay", default: 10080, null: false + t.text "import_sources" end create_table "audit_events", force: true do |t| diff --git a/doc/development/architecture.md b/doc/development/architecture.md index 541af487bb1..c00d290371e 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -56,9 +56,9 @@ To serve repositories over SSH there's an add-on application called gitlab-shell A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. By default, communication between Unicorn and the front end is via a Unix domain socket but forwarding requests via TCP is also supported. The web front end accesses `/home/git/gitlab/public` bypassing the Unicorn server to serve static pages, uploads (e.g. avatar images or attachments), and precompiled assets. GitLab serves web pages and a [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api) using the Unicorn web server. It uses Sidekiq as a job queue which, in turn, uses redis as a non-persistent database backend for job information, meta data, and incoming jobs. -The GitLab web app uses MySQL or PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository. `/home/git/gitlab-satellites` keeps checked out repositories when performing actions such as a merge request, editing files in the web interface, etc. +The GitLab web app uses MySQL or PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository. -The satellite repository is used by the web interface for editing repositories and the wiki which is also a git repository. When serving repositories over HTTP/HTTPS GitLab utilizes the GitLab API to resolve authorization and access as well as serving git objects. +When serving repositories over HTTP/HTTPS GitLab utilizes the GitLab API to resolve authorization and access as well as serving git objects. The add-on component gitlab-shell serves repositories over SSH. It manages the SSH keys within `/home/git/.ssh/authorized_keys` which should not be manually edited. gitlab-shell accesses the bare repositories directly to serve git objects and communicates with redis to submit jobs to Sidekiq for GitLab to process. gitlab-shell queries the GitLab API to determine authorization and access. @@ -129,7 +129,7 @@ Note: `/home/git/` is shorthand for `/home/git`. gitlabhq (includes Unicorn and Sidekiq logs) -- `/home/git/gitlab/log/` contains `application.log`, `production.log`, `sidekiq.log`, `unicorn.stdout.log`, `githost.log`, `satellites.log`, and `unicorn.stderr.log` normally. +- `/home/git/gitlab/log/` contains `application.log`, `production.log`, `sidekiq.log`, `unicorn.stdout.log`, `githost.log` and `unicorn.stderr.log` normally. gitlab-shell diff --git a/doc/install/installation.md b/doc/install/installation.md index c75e1d87862..73e36fa7e51 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -216,10 +216,6 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da sudo chmod -R u+rwX,go-w log/ sudo chmod -R u+rwX tmp/ - # Create directory for satellites - sudo -u git -H mkdir /home/git/gitlab-satellites - sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites - # Make sure GitLab can write to the tmp/pids/ and tmp/sockets/ directories sudo chmod -R u+rwX tmp/pids/ sudo chmod -R u+rwX tmp/sockets/ diff --git a/doc/install/requirements.md b/doc/install/requirements.md index a78590d512a..aa0d03b75bc 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -40,7 +40,7 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab ### Storage -The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least twice as much free space as all your repos combined take up. You need twice the storage because [GitLab satellites](structure.md) contain an extra copy of each repo. +The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least as much free space as all your repos combined take up. If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them. @@ -113,4 +113,4 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o ### Common UI problems with IE -If you experience UI issues with Internet Explorer, please make sure that you have the `Compatibility View` mode disabled. +If you experience UI issues with Internet Explorer, please make sure that you have the `Compatibility View` mode disabled.
\ No newline at end of file diff --git a/doc/install/structure.md b/doc/install/structure.md index 5c03f073c18..d58b0040eef 100644 --- a/doc/install/structure.md +++ b/doc/install/structure.md @@ -6,16 +6,14 @@ This is the directory structure you will end up with following the instructions | |-- git | |-- .ssh | |-- gitlab - | |-- gitlab-satellites | |-- gitlab-shell | |-- repositories * `/home/git/.ssh` - contains openssh settings. Specifically the `authorized_keys` file managed by gitlab-shell. * `/home/git/gitlab` - GitLab core software. -* `/home/git/gitlab-satellites` - checked out repositories for merge requests and file editing from web UI. This can be treated as a temporary files directory. * `/home/git/gitlab-shell` - Core add-on component of GitLab. Maintains SSH cloning and other functionality. * `/home/git/repositories` - bare repositories for all projects organized by namespace. This is where the git repositories which are pushed/pulled are maintained for all projects. **This area is critical data for projects. [Keep a backup](../raketasks/backup_restore.md)** -*Note: the default locations for gitlab-satellites and repositories can be configured in `config/gitlab.yml` of GitLab and `config.yml` of gitlab-shell.* +*Note: the default locations for repositories can be configured in `config/gitlab.yml` of GitLab and `config.yml` of gitlab-shell.* To see a more in-depth overview see the [GitLab architecture doc](../development/architecture.md). diff --git a/doc/logs/logs.md b/doc/logs/logs.md index 83c32b09253..27937e51764 100644 --- a/doc/logs/logs.md +++ b/doc/logs/logs.md @@ -51,16 +51,6 @@ December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/ error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git' ``` -#### satellites.log -This file lives in `/var/log/gitlab/gitlab-rails/satellites.log` for omnibus package or in `/home/git/gitlab/log/satellites.log` for installations from the source. - -In some cases GitLab should perform write actions to git repository, for example when it is needed to merge the merge request or edit a file with online editor. If something went wrong you can look into this file to find out what exactly happened. -``` -October 07, 2014 11:36: Failed to create satellite for Chesley Weimann III / project1817 -October 07, 2014 11:36: PID: 1872: git clone /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git /Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/conrad6841/gitlabhq -October 07, 2014 11:36: PID: 1872: -> fatal: repository '/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/repositories/conrad6841/gitlabhq.git' does not exist -``` - #### sidekiq.log This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for omnibus package or in `/home/git/gitlab/log/sidekiq.log` for installations from the source. @@ -99,4 +89,4 @@ W, [2015-02-13T07:16:01.313000 #9094] WARN -- : Unicorn::WorkerKiller send SIGQ I, [2015-02-13T07:16:01.530733 #9047] INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1 I, [2015-02-13T07:16:01.534501 #13379] INFO -- : worker=1 spawned pid=13379 I, [2015-02-13T07:16:01.534848 #13379] INFO -- : worker=1 ready -```
\ No newline at end of file +``` diff --git a/doc/profile/two_factor_authentication.md b/doc/profile/two_factor_authentication.md index f60ce35d3e2..a0e23c1586c 100644 --- a/doc/profile/two_factor_authentication.md +++ b/doc/profile/two_factor_authentication.md @@ -8,6 +8,10 @@ your phone. By enabling 2FA, the only way someone other than you can log into your account is to know your username and password *and* have access to your phone. +#### Note +When you enable 2FA, don't forget to back up your recovery codes. For your safety, if you +lose your codes for GitLab.com, we can't disable or recover them. + ## Enabling 2FA **In GitLab:** diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md index 69171cd1765..d9dce2af480 100644 --- a/doc/raketasks/maintenance.md +++ b/doc/raketasks/maintenance.md @@ -105,24 +105,11 @@ Log directory writable? ... yes Tmp directory writable? ... yes Init script exists? ... yes Init script up-to-date? ... yes -Projects have satellites? ... yes Redis version >= 2.0.0? ... yes Checking GitLab ... Finished ``` -## (Re-)Create satellite repositories - -This will create satellite repositories for all your projects. - -If necessary, remove the `repo_satellites` directory and rerun the commands below. - -``` -sudo -u git -H mkdir -p /home/git/gitlab-satellites -sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production -sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites -``` - ## Rebuild authorized_keys file In some case it is necessary to rebuild the `authorized_keys` file. diff --git a/doc/release/monthly.md b/doc/release/monthly.md index b10e7420675..12d6a84b68e 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -156,6 +156,7 @@ Tweet about the RC release: 1. Also check the CI changelog 1. Add a proposed tweet text to the blog post WIP MR description. 1. Create a WIP MR for the blog post +1. Make sure merge request title starts with `WIP` so it can not be accidently merged until ready. 1. Ask Dmitriy (or a team member with OS X) to add screenshots to the WIP MR. 1. Decide with core team who will be the MVP user. 1. Create WIP MR for adding MVP to MVP page on website diff --git a/doc/workflow/README.md b/doc/workflow/README.md index 3915198ad2a..5b8d72dfd34 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -13,4 +13,5 @@ - [Project users](add-user/add-user.md) - [Protected branches](protected_branches.md) - [Web Editor](web_editor.md) +- [Merge Requests](merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md) diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md new file mode 100644 index 00000000000..751e19da7f1 --- /dev/null +++ b/doc/workflow/merge_requests.md @@ -0,0 +1,40 @@ +# Merge Requests + +Merge requests allow you to exchange changes you made to source code + +## Checkout merge requests locally + +Locate the section for your GitLab remote in the `.git/config` file. It looks like this: + +``` +[remote "origin"] + url = https://gitlab.com/gitlab-org/gitlab-ce.git + fetch = +refs/heads/*:refs/remotes/origin/* +``` + +Now add the line `fetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*` to this section. + +It should looks like this: + +``` +[remote "origin"] + url = https://gitlab.com/gitlab-org/gitlab-ce.git + fetch = +refs/heads/*:refs/remotes/origin/* + fetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/* +``` + +Now you can fetch all the merge requests requests: + +``` +$ git fetch origin +From https://gitlab.com/gitlab-org/gitlab-ce.git + * [new ref] refs/merge-requests/1/head -> origin/merge-requests/1 + * [new ref] refs/merge-requests/2/head -> origin/merge-requests/2 +... +``` + +To check out a particular merge request: + +``` +$ git checkout origin/merge-requests/1 +``` diff --git a/features/admin/projects.feature b/features/admin/projects.feature index a6c3d6b7822..f7cec04eb75 100644 --- a/features/admin/projects.feature +++ b/features/admin/projects.feature @@ -4,9 +4,18 @@ Feature: Admin Projects Given I sign in as an admin And there are projects in system - Scenario: Projects list + Scenario: I should see non-archived projects in the list + Given archived project "Archive" When I visit admin projects page + Then I should see all non-archived projects + And I should not see project "Archive" + + Scenario: I should see all projects in the list + Given archived project "Archive" + When I visit admin projects page + And I check "Show archived projects" Then I should see all projects + And I should see "archived" label Scenario: Projects show When I visit admin projects page diff --git a/features/dashboard/new_project.feature b/features/dashboard/new_project.feature index 431dc4ccfcb..bbd82a85e3a 100644 --- a/features/dashboard/new_project.feature +++ b/features/dashboard/new_project.feature @@ -4,10 +4,27 @@ Background: Given I sign in as a user And I own project "Shop" And I visit dashboard page + And I click "New project" link @javascript Scenario: I should see New projects page - Given I click "New project" link Then I see "New project" page + Then I see all possible import optios + + @javascript + Scenario: I should see instructions on how to import from Git URL + Given I see "New project" page + When I click on "Any repo by URL" + Then I see instructions on how to import from Git URL + + @javascript + Scenario: I should see instructions on how to import from GitHub + Given I see "New project" page When I click on "Import project from GitHub" Then I see instructions on how to import from GitHub + + @javascript + Scenario: I should see Google Code import page + Given I see "New project" page + When I click on "Google Code" + Then I redirected to Google Code import page diff --git a/features/explore/projects.feature b/features/explore/projects.feature index a1b29722678..5d3870827f5 100644 --- a/features/explore/projects.feature +++ b/features/explore/projects.feature @@ -6,10 +6,12 @@ Feature: Explore Projects And private project "Enterprise" Scenario: I visit public area + Given archived project "Archive" When I visit the public projects area Then I should see project "Community" And I should not see project "Internal" And I should not see project "Enterprise" + And I should not see project "Archive" Scenario: I visit public project page When I visit project "Community" page @@ -37,11 +39,13 @@ Feature: Explore Projects And I should see empty public project details with ssh clone info Scenario: I visit public area as user - Given I sign in as a user + Given archived project "Archive" + And I sign in as a user When I visit the public projects area Then I should see project "Community" And I should see project "Internal" And I should not see project "Enterprise" + And I should not see project "Archive" Scenario: I visit internal project page as user Given I sign in as a user @@ -102,15 +106,20 @@ Feature: Explore Projects Then I should see list of merge requests for "Internal" project Scenario: Trending page - Given I sign in as a user + Given archived project "Archive" + And project "Archive" has comments + And I sign in as a user And project "Community" has comments When I visit the explore trending projects Then I should see project "Community" And I should not see project "Internal" And I should not see project "Enterprise" + And I should not see project "Archive" Scenario: Most starred page - Given I sign in as a user + Given archived project "Archive" + And I sign in as a user When I visit the explore starred projects Then I should see project "Community" And I should see project "Internal" + And I should see project "Archive" diff --git a/features/groups.feature b/features/groups.feature index 299e846edb0..d5272fdddcf 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -152,3 +152,10 @@ Feature: Groups And I click on one group milestone Then I should see group milestone with descriptions and expiry date And I should see group milestone with all issues and MRs assigned to that milestone + + # Group projects in settings + Scenario: I should see all projects in the project list in settings + Given Group "Owned" has archived project + When I visit group "Owned" projects page + Then I should see group "Owned" projects list + And I should see "archived" label diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb index 655f1895279..17233f89f38 100644 --- a/features/steps/admin/projects.rb +++ b/features/steps/admin/projects.rb @@ -2,6 +2,13 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps include SharedAuthentication include SharedPaths include SharedAdmin + include SharedProject + + step 'I should see all non-archived projects' do + Project.non_archived.each do |p| + expect(page).to have_content p.name_with_namespace + end + end step 'I should see all projects' do Project.all.each do |p| @@ -9,6 +16,15 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps end end + step 'I check "Show archived projects"' do + page.check 'Show archived projects' + click_button "Search" + end + + step 'I should see "archived" label' do + expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') + end + step 'I click on first project' do click_link Project.first.name_with_namespace end diff --git a/features/steps/dashboard/help.rb b/features/steps/dashboard/help.rb index 86ab31a58ab..800e869533e 100644 --- a/features/steps/dashboard/help.rb +++ b/features/steps/dashboard/help.rb @@ -16,6 +16,6 @@ class Spinach::Features::DashboardHelp < Spinach::FeatureSteps end step 'Header "Rebuild project satellites" should have correct ids and links' do - header_should_have_correct_id_and_link(2, '(Re-)Create satellite repositories', 're-create-satellite-repositories', '.documentation') + header_should_have_correct_id_and_link(2, 'Check GitLab configuration', 'check-gitlab-configuration', '.documentation') end end diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb index cec8d06adee..28c8c6b6015 100644 --- a/features/steps/dashboard/merge_requests.rb +++ b/features/steps/dashboard/merge_requests.rb @@ -66,7 +66,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps def authored_merge_request @authored_merge_request ||= create :merge_request, - source_branch: 'simple_merge_request', + source_branch: 'markdown', author: current_user, target_project: project, source_project: project @@ -74,14 +74,14 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps def other_merge_request @other_merge_request ||= create :merge_request, - source_branch: '2_3_notes_fix', + source_branch: 'fix', target_project: project, source_project: project end def authored_merge_request_from_fork @authored_merge_request_from_fork ||= create :merge_request, - source_branch: 'basic_page', + source_branch: 'feature_conflict', author: current_user, target_project: public_project, source_project: forked_project @@ -89,7 +89,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps def assigned_merge_request_from_fork @assigned_merge_request_from_fork ||= create :merge_request, - source_branch: 'basic_page_fix', + source_branch: 'markdown', assignee: current_user, target_project: public_project, source_project: forked_project diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb index d4440c1fb4d..1e09162a5b5 100644 --- a/features/steps/dashboard/new_project.rb +++ b/features/steps/dashboard/new_project.rb @@ -13,8 +13,17 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps expect(page).to have_content('Project path') end + step 'I see all possible import optios' do + expect(page).to have_link('GitHub') + expect(page).to have_link('Bitbucket') + expect(page).to have_link('GitLab.com') + expect(page).to have_link('Gitorious.org') + expect(page).to have_link('Google Code') + expect(page).to have_link('Any repo by URL') + end + step 'I click on "Import project from GitHub"' do - first('.how_to_import_link').click + first('.import_github').click end step 'I see instructions on how to import from GitHub' do @@ -26,4 +35,24 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps expect(element).not_to be_visible unless element == github_modal end end + + step 'I click on "Any repo by URL"' do + first('.import_git').click + end + + step 'I see instructions on how to import from Git URL' do + git_import_instructions = first('.js-toggle-content') + expect(git_import_instructions).to be_visible + expect(git_import_instructions).to have_content "Git repository URL" + expect(git_import_instructions).to have_content "The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL:" + end + + step 'I click on "Google Code"' do + first('.import_google_code').click + end + + step 'I redirected to Google Code import page' do + expect(current_path).to eq new_import_google_code_path + end + end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 46e1f4d0990..18a1c4d32ce 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -226,6 +226,15 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_link(@mr3.title, href: namespace_project_merge_request_path(@project3.namespace, @project3, @mr3)) end + step 'Group "Owned" has archived project' do + group = Group.find_by(name: 'Owned') + create(:project, namespace: group, archived: true, path: "archived-project") + end + + step 'I should see "archived" label' do + expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') + end + protected def assigned_to_me(key) diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 3e97e84d116..2a333222fb2 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -9,7 +9,6 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps @project = Project.find_by(name: "Shop") @project ||= create(:project, name: "Shop") @project.team << [@user, :reporter] - @project.ensure_satellite_exists end step 'I have a project forked off of "Shop" called "Forked Shop"' do diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 04784207a1a..778dce06359 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -198,15 +198,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'merge request "Bug NS-05" is mergeable' do - merge_request.project.satellite.create merge_request.mark_as_mergeable end step 'I accept this merge request' do - Gitlab::Satellite::MergeAction.any_instance.stub( - merge!: true, - ) - page.within '.mr-state-widget' do click_button "Accept Merge Request" end diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb index 72d873caa57..92e099315d8 100644 --- a/features/steps/shared/active_tab.rb +++ b/features/steps/shared/active_tab.rb @@ -26,7 +26,7 @@ module SharedActiveTab end step 'the active main tab should be Home' do - ensure_active_main_tab('Your Projects') + ensure_active_main_tab('Projects') end step 'the active main tab should be Projects' do diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index bb0cd9ac105..ca8fbb49101 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -39,6 +39,10 @@ module SharedPaths visit edit_group_path(Group.find_by(name: "Owned")) end + step 'I visit group "Owned" projects page' do + visit projects_group_path(Group.find_by(name: "Owned")) + end + step 'I visit group "Guest" page' do visit group_path(Group.find_by(name: "Guest")) end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 9ee2e5dfbed..ccbe8f96a4c 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -93,6 +93,29 @@ module SharedProject end # ---------------------------------------- + # Visibility of archived project + # ---------------------------------------- + + step 'archived project "Archive"' do + create :project, :public, archived: true, name: 'Archive' + end + + step 'I should not see project "Archive"' do + project = Project.find_by(name: "Archive") + expect(page).not_to have_content project.name_with_namespace + end + + step 'I should see project "Archive"' do + project = Project.find_by(name: "Archive") + expect(page).to have_content project.name_with_namespace + end + + step 'project "Archive" has comments' do + project = Project.find_by(name: "Archive") + 2.times { create(:note_on_issue, project: project) } + end + + # ---------------------------------------- # Visibility level # ---------------------------------------- diff --git a/features/support/env.rb b/features/support/env.rb index 672251af084..62c80b9c948 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -28,5 +28,9 @@ Spinach.hooks.before_run do RSpec::Mocks.setup TestEnv.init(mailer: false) + # skip pre-receive hook check so we can use + # web editor and merge + TestEnv.disable_pre_receive + include FactoryGirl::Syntax::Methods end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 09d231af41b..1f9dd6bc152 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -221,6 +221,7 @@ module API expose(:line) { |note| note.diff_new_line } expose(:line_type) { |note| note.diff_line_type } expose :author, using: Entities::UserBasic + expose :created_at end class Event < Grape::Entity diff --git a/lib/api/files.rb b/lib/api/files.rb index 83581cd3990..308c84dd135 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -3,6 +3,26 @@ module API class Files < Grape::API before { authenticate! } + helpers do + def commit_params(attrs) + { + file_path: attrs[:file_path], + current_branch: attrs[:branch_name], + target_branch: attrs[:branch_name], + commit_message: attrs[:commit_message], + file_content: attrs[:content], + file_content_encoding: attrs[:encoding] + } + end + + def commit_response(attrs) + { + file_path: attrs[:file_path], + branch_name: attrs[:branch_name], + } + end + end + resource :projects do # Get file from repository # File content is Base64 encoded @@ -73,17 +93,11 @@ module API required_attributes! [:file_path, :branch_name, :content, :commit_message] attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding] - branch_name = attrs.delete(:branch_name) - file_path = attrs.delete(:file_path) - result = ::Files::CreateService.new(user_project, current_user, attrs, branch_name, file_path).execute + result = ::Files::CreateService.new(user_project, current_user, commit_params(attrs)).execute if result[:status] == :success status(201) - - { - file_path: file_path, - branch_name: branch_name - } + commit_response(attrs) else render_api_error!(result[:message], 400) end @@ -105,17 +119,11 @@ module API required_attributes! [:file_path, :branch_name, :content, :commit_message] attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding] - branch_name = attrs.delete(:branch_name) - file_path = attrs.delete(:file_path) - result = ::Files::UpdateService.new(user_project, current_user, attrs, branch_name, file_path).execute + result = ::Files::UpdateService.new(user_project, current_user, commit_params(attrs)).execute if result[:status] == :success status(200) - - { - file_path: file_path, - branch_name: branch_name - } + commit_response(attrs) else http_status = result[:http_status] || 400 render_api_error!(result[:message], http_status) @@ -138,17 +146,11 @@ module API required_attributes! [:file_path, :branch_name, :commit_message] attrs = attributes_for_keys [:file_path, :branch_name, :commit_message] - branch_name = attrs.delete(:branch_name) - file_path = attrs.delete(:file_path) - result = ::Files::DeleteService.new(user_project, current_user, attrs, branch_name, file_path).execute + result = ::Files::DeleteService.new(user_project, current_user, commit_params(attrs)).execute if result[:status] == :success status(200) - - { - file_path: file_path, - branch_name: branch_name - } + commit_response(attrs) else render_api_error!(result[:message], 400) end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index ce21c699e8f..7412274b045 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -198,7 +198,11 @@ module API if merge_request.open? && !merge_request.work_in_progress? if merge_request.can_be_merged? - merge_request.automerge!(current_user, params[:merge_commit_message] || merge_request.merge_commit_message) + commit_message = params[:merge_commit_message] || merge_request.merge_commit_message + + ::MergeRequests::MergeService.new(merge_request.target_project, current_user). + execute(merge_request, commit_message) + present merge_request, with: Entities::MergeRequest else render_api_error!('Branch cannot be merged', 405) diff --git a/lib/gitlab.rb b/lib/gitlab.rb index 5fc1862c3e9..6108697bc20 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -1,5 +1,4 @@ require 'gitlab/git' module Gitlab - autoload :Satellite, 'gitlab/satellite/satellite' end diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 172d4902add..14ee4701e7b 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -217,20 +217,6 @@ module Gitlab FileUtils.mv(full_path(old_name), full_path(new_name)) end - # Remove GitLab Satellites for provided path (namespace or repo dir) - # - # Ex. - # rm_satellites("gitlab") - # - # rm_satellites("gitlab/gitlab-ci.git") - # - def rm_satellites(path) - raise ArgumentError.new("Path can't be blank") if path.blank? - - satellites_path = File.join(Gitlab.config.satellites.path, path) - FileUtils.rm_r(satellites_path, force: true) - end - def url_to_repo(path) Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 931d51c55d3..1a2a50a14d0 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -22,7 +22,8 @@ module Gitlab sign_in_text: Settings.extra['sign_in_text'], restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'], max_attachment_size: Settings.gitlab['max_attachment_size'], - session_expire_delay: Settings.gitlab['session_expire_delay'] + session_expire_delay: Settings.gitlab['session_expire_delay'], + import_sources: Settings.gitlab['import_sources'] ) end end diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb new file mode 100644 index 00000000000..dd393fe09d2 --- /dev/null +++ b/lib/gitlab/git/hook.rb @@ -0,0 +1,59 @@ +module Gitlab + module Git + class Hook + attr_reader :name, :repo_path, :path + + def initialize(name, repo_path) + @name = name + @repo_path = repo_path + @path = File.join(repo_path.strip, 'hooks', name) + end + + def exists? + File.exist?(path) + end + + def trigger(gl_id, oldrev, newrev, ref) + return true unless exists? + + changes = [oldrev, newrev, ref].join(" ") + + # function will return true if succesful + exit_status = false + + vars = { + 'GL_ID' => gl_id, + 'PWD' => repo_path + } + + options = { + chdir: repo_path + } + + Open3.popen2(vars, path, options) do |stdin, _, wait_thr| + exit_status = true + stdin.sync = true + + # in git, pre- and post- receive hooks may just exit without + # reading stdin. We catch the exception to avoid a broken pipe + # warning + begin + # inject all the changes as stdin to the hook + changes.lines do |line| + stdin.puts line + end + rescue Errno::EPIPE + end + + stdin.close + + unless wait_thr.value == 0 + exit_status = false + end + end + + exit_status + end + end + end +end diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb new file mode 100644 index 00000000000..991b70aab6a --- /dev/null +++ b/lib/gitlab/import_sources.rb @@ -0,0 +1,29 @@ +# Gitlab::ImportSources module +# +# Define import sources that can be used +# during the creation of new project +# +module Gitlab + module ImportSources + extend CurrentSettings + + class << self + def values + options.values + end + + def options + { + 'GitHub' => 'github', + 'Bitbucket' => 'bitbucket', + 'GitLab.com' => 'gitlab', + 'Gitorious.org' => 'gitorious', + 'Google Code' => 'google_code', + 'Any repo by URL' => 'git', + } + end + + end + + end +end diff --git a/lib/gitlab/satellite/action.rb b/lib/gitlab/satellite/action.rb deleted file mode 100644 index 489070f1a3f..00000000000 --- a/lib/gitlab/satellite/action.rb +++ /dev/null @@ -1,58 +0,0 @@ -module Gitlab - module Satellite - class Action - DEFAULT_OPTIONS = { git_timeout: Gitlab.config.satellites.timeout.seconds } - - attr_accessor :options, :project, :user - - def initialize(user, project, options = {}) - @options = DEFAULT_OPTIONS.merge(options) - @project = project - @user = user - end - - protected - - # * Sets a 30s timeout for Git - # * Locks the satellite repo - # * Yields the prepared satellite repo - def in_locked_and_timed_satellite - Gitlab::ShellEnv.set_env(user) - - Grit::Git.with_timeout(options[:git_timeout]) do - project.satellite.lock do - return yield project.satellite.repo - end - end - rescue Errno::ENOMEM => ex - return handle_exception(ex) - rescue Grit::Git::GitTimeout => ex - return handle_exception(ex) - ensure - Gitlab::ShellEnv.reset_env - end - - # * Recreates the satellite - # * Sets up Git variables for the user - # - # Note: use this within #in_locked_and_timed_satellite - def prepare_satellite!(repo) - project.satellite.clear_and_update! - - if user - repo.config['user.name'] = user.name - repo.config['user.email'] = user.email - end - end - - def default_options(options = {}) - { raise: true, timeout: true }.merge(options) - end - - def handle_exception(exception) - Gitlab::GitLogger.error(exception.message) - false - end - end - end -end diff --git a/lib/gitlab/satellite/compare_action.rb b/lib/gitlab/satellite/compare_action.rb deleted file mode 100644 index 46c98a8f4ca..00000000000 --- a/lib/gitlab/satellite/compare_action.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Gitlab - module Satellite - class BranchesWithoutParent < StandardError; end - - class CompareAction < Action - def initialize(user, target_project, target_branch, source_project, source_branch) - super user, target_project - - @target_project, @target_branch = target_project, target_branch - @source_project, @source_branch = source_project, source_branch - end - - # Compare 2 repositories and return Gitlab::CompareResult object - def result - in_locked_and_timed_satellite do |target_repo| - prepare_satellite!(target_repo) - update_satellite_source_and_target!(target_repo) - - Gitlab::CompareResult.new(compare(target_repo)) - end - rescue Grit::Git::CommandFailed => ex - raise BranchesWithoutParent - end - - private - - # Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for diffs - def update_satellite_source_and_target!(target_repo) - target_repo.remote_add('source', @source_project.repository.path_to_repo) - target_repo.remote_fetch('source') - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - - def compare(repo) - @compare ||= Gitlab::Git::Compare.new( - Gitlab::Git::Repository.new(repo.path), - "origin/#{@target_branch}", - "source/#{@source_branch}" - ) - end - end - end -end diff --git a/lib/gitlab/satellite/files/delete_file_action.rb b/lib/gitlab/satellite/files/delete_file_action.rb deleted file mode 100644 index 0d37b9dea85..00000000000 --- a/lib/gitlab/satellite/files/delete_file_action.rb +++ /dev/null @@ -1,50 +0,0 @@ -require_relative 'file_action' - -module Gitlab - module Satellite - class DeleteFileAction < FileAction - # Deletes file and creates a new commit for it - # - # Returns false if committing the change fails - # Returns false if pushing from the satellite to bare repo failed or was rejected - # Returns true otherwise - def commit!(content, commit_message) - in_locked_and_timed_satellite do |repo| - prepare_satellite!(repo) - - # create target branch in satellite at the corresponding commit from bare repo - repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}") - - # update the file in the satellite's working dir - file_path_in_satellite = File.join(repo.working_dir, file_path) - - # Prevent relative links - unless safe_path?(file_path_in_satellite) - Gitlab::GitLogger.error("FileAction: Relative path not allowed") - return false - end - - File.delete(file_path_in_satellite) - - # add removed file - repo.remove(file_path_in_satellite) - - # commit the changes - # will raise CommandFailed when commit fails - repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) - - - # push commit back to bare repo - # will raise CommandFailed when push fails - repo.git.push({ raise: true, timeout: true }, :origin, ref) - - # everything worked - true - end - rescue Grit::Git::CommandFailed => ex - Gitlab::GitLogger.error(ex.message) - false - end - end - end -end diff --git a/lib/gitlab/satellite/files/edit_file_action.rb b/lib/gitlab/satellite/files/edit_file_action.rb deleted file mode 100644 index 3cb9c0b5ecb..00000000000 --- a/lib/gitlab/satellite/files/edit_file_action.rb +++ /dev/null @@ -1,68 +0,0 @@ -require_relative 'file_action' - -module Gitlab - module Satellite - # GitLab server-side file update and commit - class EditFileAction < FileAction - # Updates the files content and creates a new commit for it - # - # Returns false if the ref has been updated while editing the file - # Returns false if committing the change fails - # Returns false if pushing from the satellite to bare repo failed or was rejected - # Returns true otherwise - def commit!(content, commit_message, encoding, new_branch = nil) - in_locked_and_timed_satellite do |repo| - prepare_satellite!(repo) - - # create target branch in satellite at the corresponding commit from bare repo - begin - repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}") - rescue Grit::Git::CommandFailed => ex - log_and_raise(CheckoutFailed, ex.message) - end - - # update the file in the satellite's working dir - file_path_in_satellite = File.join(repo.working_dir, file_path) - - # Prevent relative links - unless safe_path?(file_path_in_satellite) - Gitlab::GitLogger.error("FileAction: Relative path not allowed") - return false - end - - # Write file - write_file(file_path_in_satellite, content, encoding) - - # commit the changes - # will raise CommandFailed when commit fails - begin - repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) - rescue Grit::Git::CommandFailed => ex - log_and_raise(CommitFailed, ex.message) - end - - - target_branch = new_branch.present? ? "#{ref}:#{new_branch}" : ref - - # push commit back to bare repo - # will raise CommandFailed when push fails - begin - repo.git.push({ raise: true, timeout: true }, :origin, target_branch) - rescue Grit::Git::CommandFailed => ex - log_and_raise(PushFailed, ex.message) - end - - # everything worked - true - end - end - - private - - def log_and_raise(errorClass, message) - Gitlab::GitLogger.error(message) - raise(errorClass, message) - end - end - end -end diff --git a/lib/gitlab/satellite/files/file_action.rb b/lib/gitlab/satellite/files/file_action.rb deleted file mode 100644 index 6446b14568a..00000000000 --- a/lib/gitlab/satellite/files/file_action.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Gitlab - module Satellite - class FileAction < Action - attr_accessor :file_path, :ref - - def initialize(user, project, ref, file_path) - super user, project - @file_path = file_path - @ref = ref - end - - def safe_path?(path) - File.absolute_path(path) == path - end - - def write_file(abs_file_path, content, file_encoding = 'text') - if file_encoding == 'base64' - File.open(abs_file_path, 'wb') { |f| f.write(Base64.decode64(content)) } - else - File.open(abs_file_path, 'w') { |f| f.write(content) } - end - end - end - end -end diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb deleted file mode 100644 index 724dfa0d042..00000000000 --- a/lib/gitlab/satellite/files/new_file_action.rb +++ /dev/null @@ -1,67 +0,0 @@ -require_relative 'file_action' - -module Gitlab - module Satellite - class NewFileAction < FileAction - # Updates the files content and creates a new commit for it - # - # Returns false if the ref has been updated while editing the file - # Returns false if committing the change fails - # Returns false if pushing from the satellite to bare repo failed or was rejected - # Returns true otherwise - def commit!(content, commit_message, encoding, new_branch = nil) - in_locked_and_timed_satellite do |repo| - prepare_satellite!(repo) - - # create target branch in satellite at the corresponding commit from bare repo - current_ref = - if @project.empty_repo? - # skip this step if we want to add first file to empty repo - Satellite::PARKING_BRANCH - else - repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}") - ref - end - - file_path_in_satellite = File.join(repo.working_dir, file_path) - dir_name_in_satellite = File.dirname(file_path_in_satellite) - - # Prevent relative links - unless safe_path?(file_path_in_satellite) - Gitlab::GitLogger.error("FileAction: Relative path not allowed") - return false - end - - # Create dir if not exists - FileUtils.mkdir_p(dir_name_in_satellite) - - # Write file - write_file(file_path_in_satellite, content, encoding) - - # add new file - repo.add(file_path_in_satellite) - - # commit the changes - # will raise CommandFailed when commit fails - repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) - - target_branch = if new_branch.present? && !@project.empty_repo? - "#{ref}:#{new_branch}" - else - "#{current_ref}:#{ref}" - end - - # push commit back to bare repo - # will raise CommandFailed when push fails - repo.git.push({ raise: true, timeout: true }, :origin, target_branch) - - # everything worked - true - end - rescue Grit::Git::CommandFailed => ex - Gitlab::GitLogger.error(ex.message) - false - end - end - end -end diff --git a/lib/gitlab/satellite/logger.rb b/lib/gitlab/satellite/logger.rb deleted file mode 100644 index 6f3f8255aca..00000000000 --- a/lib/gitlab/satellite/logger.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Gitlab - module Satellite - class Logger < Gitlab::Logger - def self.file_name - 'satellites.log' - end - - def format_message(severity, timestamp, progname, msg) - "#{timestamp.to_s(:long)}: #{msg}\n" - end - end - end -end diff --git a/lib/gitlab/satellite/merge_action.rb b/lib/gitlab/satellite/merge_action.rb deleted file mode 100644 index 52e8130956c..00000000000 --- a/lib/gitlab/satellite/merge_action.rb +++ /dev/null @@ -1,147 +0,0 @@ -module Gitlab - module Satellite - # GitLab server-side merge - class MergeAction < Action - attr_accessor :merge_request - - def initialize(user, merge_request) - super user, merge_request.target_project - @merge_request = merge_request - end - - # Checks if a merge request can be executed without user interaction - def can_be_merged? - in_locked_and_timed_satellite do |merge_repo| - prepare_satellite!(merge_repo) - merge_in_satellite!(merge_repo) - end - end - - # Merges the source branch into the target branch in the satellite and - # pushes it back to the repository. - # It also removes the source branch if requested in the merge request (and this is permitted by the merge request). - # - # Returns false if the merge produced conflicts - # Returns false if pushing from the satellite to the repository failed or was rejected - # Returns true otherwise - def merge!(merge_commit_message = nil) - in_locked_and_timed_satellite do |merge_repo| - prepare_satellite!(merge_repo) - if merge_in_satellite!(merge_repo, merge_commit_message) - # push merge back to bare repo - # will raise CommandFailed when push fails - merge_repo.git.push(default_options, :origin, merge_request.target_branch) - - # remove source branch - if merge_request.remove_source_branch? - # will raise CommandFailed when push fails - merge_repo.git.push(default_options, :origin, ":#{merge_request.source_branch}") - merge_request.source_project.repository.expire_branch_names - end - # merge, push and branch removal successful - true - end - end - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - - def diff_in_satellite - in_locked_and_timed_satellite do |merge_repo| - prepare_satellite!(merge_repo) - update_satellite_source_and_target!(merge_repo) - - # Only show what is new in the source branch compared to the target branch, not the other way around. - # The line below with merge_base is equivalent to diff with three dots (git diff branch1...branch2) - # From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B" - common_commit = merge_repo.git.native(:merge_base, default_options, ["origin/#{merge_request.target_branch}", "source/#{merge_request.source_branch}"]).strip - merge_repo.git.native(:diff, default_options, common_commit, "source/#{merge_request.source_branch}") - end - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - - def diffs_between_satellite - in_locked_and_timed_satellite do |merge_repo| - prepare_satellite!(merge_repo) - update_satellite_source_and_target!(merge_repo) - if merge_request.for_fork? - repository = Gitlab::Git::Repository.new(merge_repo.path) - diffs = Gitlab::Git::Diff.between( - repository, - "source/#{merge_request.source_branch}", - "origin/#{merge_request.target_branch}" - ) - else - raise "Attempt to determine diffs between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]" - end - - return diffs - end - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - - # Get commit as an email patch - def format_patch - in_locked_and_timed_satellite do |merge_repo| - prepare_satellite!(merge_repo) - update_satellite_source_and_target!(merge_repo) - patch = merge_repo.git.format_patch(default_options({ stdout: true }), "origin/#{merge_request.target_branch}..source/#{merge_request.source_branch}") - end - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - - # Retrieve an array of commits between the source and the target - def commits_between - in_locked_and_timed_satellite do |merge_repo| - prepare_satellite!(merge_repo) - update_satellite_source_and_target!(merge_repo) - if merge_request.for_fork? - repository = Gitlab::Git::Repository.new(merge_repo.path) - commits = Gitlab::Git::Commit.between( - repository, - "origin/#{merge_request.target_branch}", - "source/#{merge_request.source_branch}" - ) - else - raise "Attempt to determine commits between for a non forked merge request in satellite MergeRequest.id:[#{merge_request.id}]" - end - - return commits - end - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - - private - # Merges the source_branch into the target_branch in the satellite. - # - # Note: it will clear out the satellite before doing anything - # - # Returns false if the merge produced conflicts - # Returns true otherwise - def merge_in_satellite!(repo, message = nil) - update_satellite_source_and_target!(repo) - - message ||= "Merge branch '#{merge_request.source_branch}' into '#{merge_request.target_branch}'" - - # merge the source branch into the satellite - # will raise CommandFailed when merge fails - repo.git.merge(default_options({ no_ff: true }), "-m#{message}", "source/#{merge_request.source_branch}") - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - - # Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for merges, diffs etc - def update_satellite_source_and_target!(repo) - repo.remote_add('source', merge_request.source_project.repository.path_to_repo) - repo.remote_fetch('source') - repo.git.checkout(default_options({ b: true }), merge_request.target_branch, "origin/#{merge_request.target_branch}") - rescue Grit::Git::CommandFailed => ex - handle_exception(ex) - end - end - end -end diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb deleted file mode 100644 index 398643d68de..00000000000 --- a/lib/gitlab/satellite/satellite.rb +++ /dev/null @@ -1,148 +0,0 @@ -module Gitlab - module Satellite - autoload :DeleteFileAction, 'gitlab/satellite/files/delete_file_action' - autoload :EditFileAction, 'gitlab/satellite/files/edit_file_action' - autoload :FileAction, 'gitlab/satellite/files/file_action' - autoload :NewFileAction, 'gitlab/satellite/files/new_file_action' - - class CheckoutFailed < StandardError; end - class CommitFailed < StandardError; end - class PushFailed < StandardError; end - - class Satellite - include Gitlab::Popen - - PARKING_BRANCH = "__parking_branch" - - attr_accessor :project - - def initialize(project) - @project = project - end - - def log(message) - Gitlab::Satellite::Logger.error(message) - end - - def clear_and_update! - project.ensure_satellite_exists - - @repo = nil - clear_working_dir! - delete_heads! - remove_remotes! - update_from_source! - end - - def create - 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}") - log("PID: #{project.id}: -> #{output}") - - if status.zero? - true - else - log("Failed to create satellite for #{project.name_with_namespace}") - false - end - end - - def exists? - File.exists? path - end - - # * Locks the satellite - # * Changes the current directory to the satellite's working dir - # * Yields - def lock - project.ensure_satellite_exists - - File.open(lock_file, "w+") do |f| - begin - f.flock File::LOCK_EX - yield - ensure - f.flock File::LOCK_UN - end - end - end - - def lock_file - create_locks_dir unless File.exists?(lock_files_dir) - File.join(lock_files_dir, "satellite_#{project.id}.lock") - end - - def path - File.join(Gitlab.config.satellites.path, project.path_with_namespace) - end - - def repo - project.ensure_satellite_exists - - @repo ||= Grit::Repo.new(path) - end - - def destroy - FileUtils.rm_rf(path) - end - - private - - # Clear the working directory - def clear_working_dir! - repo.git.reset(hard: true) - repo.git.clean(f: true, d: true, x: true) - end - - # Deletes all branches except the parking branch - # - # This ensures we have no name clashes or issues updating branches when - # working with the satellite. - def delete_heads! - heads = repo.heads.map(&:name) - - # update or create the parking branch - repo.git.checkout(default_options({ B: true }), PARKING_BRANCH) - - # remove the parking branch from the list of heads ... - heads.delete(PARKING_BRANCH) - # ... and delete all others - heads.each { |head| repo.git.branch(default_options({ D: true }), head) } - end - - # Deletes all remotes except origin - # - # This ensures we have no remote name clashes or issues updating branches when - # working with the satellite. - def remove_remotes! - remotes = repo.git.remote.split(' ') - remotes.delete('origin') - remotes.each { |name| repo.git.remote(default_options,'rm', name)} - end - - # Updates the satellite from bare repo - # - # Note: this will only update remote branches (i.e. origin/*) - def update_from_source! - repo.git.remote(default_options, 'set-url', :origin, project.repository.path_to_repo) - repo.git.fetch(default_options, :origin) - end - - def default_options(options = {}) - { raise: true, timeout: true }.merge(options) - end - - # Create directory for storing - # satellites lock files - def create_locks_dir - FileUtils.mkdir_p(lock_files_dir) - end - - def lock_files_dir - @lock_files_dir ||= File.join(Gitlab.config.satellites.path, "tmp") - end - end - end -end diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index d19695f92fb..80ee572938d 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -25,7 +25,6 @@ namespace :gitlab do check_init_script_exists check_init_script_up_to_date check_projects_have_namespace - check_satellites_exist check_redis_version check_ruby_version check_git_version @@ -238,37 +237,6 @@ namespace :gitlab do end end - def check_satellites_exist - print "Projects have satellites? ... " - - unless Project.count > 0 - puts "can't check, you have no projects".magenta - return - end - puts "" - - Project.find_each(batch_size: 100) do |project| - print sanitized_message(project) - - if project.satellite.exists? - puts "yes".green - elsif project.empty_repo? - puts "can't create, repository is empty".magenta - else - puts "no".red - try_fixing_it( - sudo_gitlab("bundle exec rake gitlab:satellites:create RAILS_ENV=production"), - "If necessary, remove the tmp/repo_satellites directory ...", - "... and rerun the above command" - ) - for_more_information( - "doc/raketasks/maintenance.md " - ) - fix_and_rerun - end - end - end - def check_log_writable print "Log directory writable? ... " @@ -339,7 +307,6 @@ namespace :gitlab do check_repo_base_is_not_symlink check_repo_base_user_and_group check_repo_base_permissions - check_satellites_permissions check_repos_hooks_directory_is_link check_gitlab_shell_self_test @@ -417,29 +384,6 @@ namespace :gitlab do end end - def check_satellites_permissions - print "Satellites access is drwxr-x---? ... " - - satellites_path = Gitlab.config.satellites.path - unless File.exists?(satellites_path) - puts "can't check because of previous errors".magenta - return - end - - if File.stat(satellites_path).mode.to_s(8).ends_with?("0750") - puts "yes".green - else - puts "no".red - try_fixing_it( - "sudo chmod u+rwx,g=rx,o-rwx #{satellites_path}", - ) - for_more_information( - see_installation_guide_section "GitLab" - ) - fix_and_rerun - end - end - def check_repo_base_user_and_group gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group diff --git a/lib/tasks/gitlab/enable_automerge.rake b/lib/tasks/gitlab/enable_automerge.rake deleted file mode 100644 index 3dade9d75b8..00000000000 --- a/lib/tasks/gitlab/enable_automerge.rake +++ /dev/null @@ -1,39 +0,0 @@ -namespace :gitlab do - namespace :satellites do - desc "GitLab | Create satellite repos" - task create: :environment do - create_satellites - end - end - - def create_satellites - warn_user_is_not_gitlab - - print "Creating satellites for ..." - unless Project.count > 0 - puts "skipping, because you have no projects".magenta - return - end - puts "" - - Project.find_each(batch_size: 100) do |project| - print "#{project.name_with_namespace.yellow} ... " - - unless project.repo_exists? - puts "skipping, because the repo is empty".magenta - next - end - - if project.satellite.exists? - puts "exists already".green - else - print "\n... " - if project.satellite.create - puts "created".green - else - puts "error".red - end - end - end - end -end diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index f921dd9cc09..088e34f050c 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -25,7 +25,7 @@ describe Gitlab::ReferenceExtractor do project.team << [@u_bar, :guest] subject.analyze(%Q{ - Inline code: `@foo` + Inline code: `@foo` Code block: @@ -33,7 +33,7 @@ describe Gitlab::ReferenceExtractor do @bar ``` - Quote: + Quote: > @offteam }) @@ -49,8 +49,8 @@ describe Gitlab::ReferenceExtractor do end it 'accesses valid merge requests' do - @m0 = create(:merge_request, source_project: project, target_project: project, source_branch: 'aaa') - @m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'bbb') + @m0 = create(:merge_request, source_project: project, target_project: project, source_branch: 'markdown') + @m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'feature_conflict') subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.") expect(subject.merge_requests).to eq([@m1, @m0]) diff --git a/spec/lib/gitlab/satellite/action_spec.rb b/spec/lib/gitlab/satellite/action_spec.rb deleted file mode 100644 index 0a93676edc3..00000000000 --- a/spec/lib/gitlab/satellite/action_spec.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'spec_helper' - -describe 'Gitlab::Satellite::Action' do - let(:project) { create(:project) } - let(:user) { create(:user) } - - describe '#prepare_satellite!' do - it 'should be able to fetch timeout from conf' do - expect(Gitlab::Satellite::Action::DEFAULT_OPTIONS[:git_timeout]).to eq(30.seconds) - end - - it 'create a repository with a parking branch and one remote: origin' do - repo = project.satellite.repo - - #now lets dirty it up - - starting_remote_count = repo.git.list_remotes.size - expect(starting_remote_count).to be >= 1 - #kind of hookey way to add a second remote - origin_uri = repo.git.remote({ v: true }).split(" ")[1] - - repo.git.remote({ raise: true }, 'add', 'another-remote', origin_uri) - repo.git.branch({ raise: true }, 'a-new-branch') - - expect(repo.heads.size).to be > (starting_remote_count) - expect(repo.git.remote().split(" ").size).to be > (starting_remote_count) - - repo.git.config({}, "user.name", "#{user.name} -- foo") - repo.git.config({}, "user.email", "#{user.email} -- foo") - expect(repo.config['user.name']).to eq("#{user.name} -- foo") - expect(repo.config['user.email']).to eq("#{user.email} -- foo") - - - #These must happen in the context of the satellite directory... - satellite_action = Gitlab::Satellite::Action.new(user, project) - project.satellite.lock do - #Now clean it up, use send to get around prepare_satellite! being protected - satellite_action.send(:prepare_satellite!, repo) - end - - #verify it's clean - heads = repo.heads.map(&:name) - expect(heads.size).to eq(1) - expect(heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH)).to eq(true) - remotes = repo.git.remote().split(' ') - expect(remotes.size).to eq(1) - expect(remotes.include?('origin')).to eq(true) - expect(repo.config['user.name']).to eq(user.name) - expect(repo.config['user.email']).to eq(user.email) - end - end - - describe '#in_locked_and_timed_satellite' do - - it 'should make use of a lockfile' do - repo = project.satellite.repo - called = false - - #set assumptions - FileUtils.rm_f(project.satellite.lock_file) - - expect(File.exists?(project.satellite.lock_file)).to be_falsey - - satellite_action = Gitlab::Satellite::Action.new(user, project) - satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| - expect(repo).to eq(sat_repo) - expect(File.exists? project.satellite.lock_file).to be_truthy - called = true - end - - expect(called).to be_truthy - - end - - it 'should be able to use the satellite after locking' do - repo = project.satellite.repo - called = false - - # Set base assumptions - if File.exists? project.satellite.lock_file - expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey - end - - satellite_action = Gitlab::Satellite::Action.new(user, project) - satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| - called = true - expect(repo).to eq(sat_repo) - expect(File.exists? project.satellite.lock_file).to be_truthy - expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_truthy - end - - expect(called).to be_truthy - expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey - - end - - class FileLockStatusChecker < File - def flocked?(&block) - status = flock LOCK_EX|LOCK_NB - case status - when false - return true - when 0 - begin - block ? block.call : false - ensure - flock LOCK_UN - end - else - raise SystemCallError, status - end - end - end - - end -end diff --git a/spec/lib/gitlab/satellite/merge_action_spec.rb b/spec/lib/gitlab/satellite/merge_action_spec.rb deleted file mode 100644 index e977261c726..00000000000 --- a/spec/lib/gitlab/satellite/merge_action_spec.rb +++ /dev/null @@ -1,118 +0,0 @@ -require 'spec_helper' - -describe 'Gitlab::Satellite::MergeAction' do - include RepoHelpers - - let(:project) { create(:project, namespace: create(:group)) } - let(:fork_project) { create(:project, namespace: create(:group), forked_from_project: project) } - let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let(:merge_request_fork) { create(:merge_request, source_project: fork_project, target_project: project) } - - let(:merge_request_with_conflict) { create(:merge_request, :conflict, source_project: project, target_project: project) } - let(:merge_request_fork_with_conflict) { create(:merge_request, :conflict, source_project: project, target_project: project) } - - describe '#commits_between' do - def verify_commits(commits, first_commit_sha, last_commit_sha) - commits.each { |commit| expect(commit.class).to eq(Gitlab::Git::Commit) } - expect(commits.first.id).to eq(first_commit_sha) - expect(commits.last.id).to eq(last_commit_sha) - end - - context 'on fork' do - it 'should get proper commits between' do - commits = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).commits_between - verify_commits(commits, sample_compare.commits.first, sample_compare.commits.last) - end - end - - context 'between branches' do - it 'should raise exception -- not expected to be used by non forks' do - expect { Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between }.to raise_error(RuntimeError) - end - end - end - - describe '#format_patch' do - def verify_content(patch) - sample_compare.commits.each do |commit| - expect(patch.include?(commit)).to be_truthy - end - end - - context 'on fork' do - it 'should build a format patch' do - patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).format_patch - verify_content(patch) - end - end - - context 'between branches' do - it 'should build a format patch' do - patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request).format_patch - verify_content(patch) - end - end - end - - describe '#diffs_between_satellite tested against diff_in_satellite' do - def is_a_matching_diff(diff, diffs) - diff_count = diff.scan('diff --git').size - expect(diff_count).to be >= 1 - expect(diffs.size).to eq(diff_count) - diffs.each do |a_diff| - expect(a_diff.class).to eq(Gitlab::Git::Diff) - expect(diff.include? a_diff.diff).to be_truthy - end - end - - context 'on fork' do - it 'should get proper diffs' do - diffs = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).diffs_between_satellite - diff = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request_fork).diff_in_satellite - is_a_matching_diff(diff, diffs) - end - end - - context 'between branches' do - it 'should get proper diffs' do - expect{ Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite }.to raise_error(RuntimeError) - end - end - end - - describe '#can_be_merged?' do - context 'on fork' do - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).can_be_merged?).to be_truthy - end - - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request_fork_with_conflict.author, merge_request_fork_with_conflict).can_be_merged?).to be_falsey - end - end - - context 'between branches' do - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).can_be_merged?).to be_truthy - end - - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request_with_conflict.author, merge_request_with_conflict).can_be_merged?).to be_falsey - end - end - end - - describe '#merge!' do - let(:merge_request) { create(:merge_request, source_project: project, target_project: project, source_branch: "markdown", should_remove_source_branch: true) } - let(:merge_action) { Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request) } - - it 'clears cache of source repo after removing source branch' do - project.repository.expire_branch_names - expect(project.repository.branch_names).to include('markdown') - - merge_action.merge! - - expect(project.repository.branch_names).not_to include('markdown') - end - end -end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 76f6d8c54c4..b91687bc09f 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -165,7 +165,7 @@ describe MergeRequest do end it_behaves_like 'an editable mentionable' do - subject { create(:merge_request, source_project: project) } + subject { create(:merge_request) } let(:backref_text) { "merge request #{subject.to_reference}" } let(:set_mentionable_text) { ->(txt){ subject.description = txt } } diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index 69466b11f09..97b60e19e40 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -67,7 +67,7 @@ describe SlackService do opts = { title: 'Awesome merge_request', description: 'please fix', - source_branch: 'stable', + source_branch: 'feature', target_branch: 'master' } merge_service = MergeRequests::CreateService.new(project, diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5d40754d59d..2fcbd5ae108 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -91,7 +91,6 @@ describe Project do describe 'Respond to' do it { is_expected.to respond_to(:url_to_repo) } it { is_expected.to respond_to(:repo_exists?) } - it { is_expected.to respond_to(:satellite) } it { is_expected.to respond_to(:update_merge_requests) } it { is_expected.to respond_to(:execute_hooks) } it { is_expected.to respond_to(:name_with_namespace) } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 8e2849691ff..05e51532eb8 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -40,6 +40,20 @@ describe Repository do it { is_expected.to be_truthy } end + end + + describe :can_be_merged? do + context 'mergeable branches' do + subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') } + + it { is_expected.to be_truthy } + end + + context 'non-mergeable branches' do + subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') } + + it { is_expected.to be_falsey } + end context 'non merged branch' do subject { repository.merged_to_root_ref?('fix') } diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 78f2cb56b02..042e6352567 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -49,8 +49,6 @@ describe API::API, api: true do end it "should create a new file in project repo" do - expect_any_instance_of(Gitlab::Satellite::NewFileAction).to receive(:commit!).and_return(true) - post api("/projects/#{project.id}/repository/files", user), valid_params expect(response.status).to eq(201) expect(json_response['file_path']).to eq('newfile.rb') @@ -61,8 +59,9 @@ describe API::API, api: true do expect(response.status).to eq(400) end - it "should return a 400 if satellite fails to create file" do - expect_any_instance_of(Gitlab::Satellite::NewFileAction).to receive(:commit!).and_return(false) + it "should return a 400 if editor fails to create file" do + allow_any_instance_of(Repository).to receive(:commit_file). + and_return(false) post api("/projects/#{project.id}/repository/files", user), valid_params expect(response.status).to eq(400) @@ -80,8 +79,6 @@ describe API::API, api: true do end it "should update existing file in project repo" do - expect_any_instance_of(Gitlab::Satellite::EditFileAction).to receive(:commit!).and_return(true) - put api("/projects/#{project.id}/repository/files", user), valid_params expect(response.status).to eq(200) expect(json_response['file_path']).to eq(file_path) @@ -91,32 +88,6 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/files", user) expect(response.status).to eq(400) end - - it 'should return a 400 if the checkout fails' do - expect_any_instance_of(Gitlab::Satellite::EditFileAction).to receive(:commit!).and_raise(Gitlab::Satellite::CheckoutFailed) - - put api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(400) - - ref = valid_params[:branch_name] - expect(response.body).to match("ref '#{ref}' could not be checked out") - end - - it 'should return a 409 if the file was not modified' do - expect_any_instance_of(Gitlab::Satellite::EditFileAction).to receive(:commit!).and_raise(Gitlab::Satellite::CommitFailed) - - put api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(409) - expect(response.body).to match("Maybe there was nothing to commit?") - end - - it 'should return a 409 if the push fails' do - expect_any_instance_of(Gitlab::Satellite::EditFileAction).to receive(:commit!).and_raise(Gitlab::Satellite::PushFailed) - - put api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(409) - expect(response.body).to match("Maybe the file was changed by another process?") - end end describe "DELETE /projects/:id/repository/files" do @@ -129,7 +100,6 @@ describe API::API, api: true do end it "should delete existing file in project repo" do - expect_any_instance_of(Gitlab::Satellite::DeleteFileAction).to receive(:commit!).and_return(true) delete api("/projects/#{project.id}/repository/files", user), valid_params expect(response.status).to eq(200) expect(json_response['file_path']).to eq(file_path) @@ -140,8 +110,8 @@ describe API::API, api: true do expect(response.status).to eq(400) end - it "should return a 400 if satellite fails to create file" do - expect_any_instance_of(Gitlab::Satellite::DeleteFileAction).to receive(:commit!).and_return(false) + it "should return a 400 if fails to create file" do + allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) delete api("/projects/#{project.id}/repository/files", user), valid_params expect(response.status).to eq(400) diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 29db035b2de..942768fa254 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -148,7 +148,7 @@ describe API::API, api: true do it "should return merge_request" do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', - source_branch: 'stable', + source_branch: 'feature_conflict', target_branch: 'master', author: user, labels: 'label, label2' @@ -171,20 +171,20 @@ describe API::API, api: true do it "should return 400 when target_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), - title: "Test merge_request", source_branch: "stable", author: user + title: "Test merge_request", source_branch: "markdown", author: user expect(response.status).to eq(400) end it "should return 400 when title is missing" do post api("/projects/#{project.id}/merge_requests", user), - target_branch: 'master', source_branch: 'stable' + target_branch: 'master', source_branch: 'markdown' expect(response.status).to eq(400) end it 'should return 400 on invalid label names' do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', - source_branch: 'stable', + source_branch: 'markdown', target_branch: 'master', author: user, labels: 'label, ?' @@ -198,7 +198,7 @@ describe API::API, api: true do before do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', - source_branch: 'stable', + source_branch: 'feature_conflict', target_branch: 'master', author: user @mr = MergeRequest.all.last @@ -208,7 +208,7 @@ describe API::API, api: true do expect do post api("/projects/#{project.id}/merge_requests", user), title: 'New test merge_request', - source_branch: 'stable', + source_branch: 'feature_conflict', target_branch: 'master', author: user end.to change { MergeRequest.count }.by(0) @@ -228,7 +228,8 @@ describe API::API, api: true do it "should return merge_request" do post api("/projects/#{fork_project.id}/merge_requests", user2), - title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' + title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", + author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' expect(response.status).to eq(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['description']).to eq('Test description for Test merge_request') @@ -258,7 +259,7 @@ describe API::API, api: true do it "should return 400 when title is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), - target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id + target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id expect(response.status).to eq(400) end @@ -267,7 +268,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', target_branch: 'master', - source_branch: 'stable', + source_branch: 'markdown', author: user, target_project_id: fork_project.id expect(response.status).to eq(422) @@ -277,7 +278,7 @@ describe API::API, api: true do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: 'master', - source_branch: 'stable', + source_branch: 'markdown', author: user2, target_project_id: unrelated_project.id expect(response.status).to eq(422) @@ -286,7 +287,7 @@ describe API::API, api: true do it "should return 201 when target_branch is specified and for the same project" do post api("/projects/#{fork_project.id}/merge_requests", user2), - title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id + title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id expect(response.status).to eq(201) end end @@ -302,9 +303,6 @@ describe API::API, api: true do describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do it "should return merge_request in case of success" do - allow_any_instance_of(MergeRequest). - to receive_messages(can_be_merged?: true, automerge!: true) - put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) expect(response.status).to eq(200) diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 0040718d9be..82f62a8709c 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -210,8 +210,8 @@ end # diffs_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs # commits_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/commits(.:format) projects/merge_requests#commits -# automerge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/automerge(.:format) projects/merge_requests#automerge -# automerge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/automerge_check(.:format) projects/merge_requests#automerge_check +# merge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/merge(.:format) projects/merge_requests#merge +# merge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/merge_check(.:format) projects/merge_requests#merge_check # ci_status_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/ci_status(.:format) projects/merge_requests#ci_status # toggle_subscription_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/toggle_subscription(.:format) projects/merge_requests#toggle_subscription # branch_from_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from @@ -233,15 +233,15 @@ describe Projects::MergeRequestsController, 'routing' do expect(get('/gitlab/gitlabhq/merge_requests/1/commits')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end - it 'to #automerge' do - expect(post('/gitlab/gitlabhq/merge_requests/1/automerge')).to route_to( - 'projects/merge_requests#automerge', + it 'to #merge' do + expect(post('/gitlab/gitlabhq/merge_requests/1/merge')).to route_to( + 'projects/merge_requests#merge', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1' ) end - it 'to #automerge_check' do - expect(get('/gitlab/gitlabhq/merge_requests/1/automerge_check')).to route_to('projects/merge_requests#automerge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') + it 'to #merge_check' do + expect(get('/gitlab/gitlabhq/merge_requests/1/merge_check')).to route_to('projects/merge_requests#merge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #branch_from' do diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index d9bfdf64308..cc64d69361e 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -10,7 +10,7 @@ describe MergeRequests::CreateService do { title: 'Awesome merge_request', description: 'please fix', - source_branch: 'stable', + source_branch: 'feature', target_branch: 'master' } end diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 0a25fb12f4e..7b564d34d7b 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -24,11 +24,6 @@ describe MergeRequests::MergeService do it { expect(merge_request).to be_valid } it { expect(merge_request).to be_merged } - it 'should execute hooks with merge action' do - expect(service).to have_received(:execute_hooks). - with(merge_request, 'merge') - end - it 'should send email to user2 about merge of new merge_request' do email = ActionMailer::Base.deliveries.last expect(email.to.first).to eq(user2.email) diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index f0717e61781..e3de0afb448 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -9,7 +9,7 @@ def common_mentionable_setup let(:author) { subject.author } let(:mentioned_issue) { create(:issue, project: project) } - let(:mentioned_mr) { create(:merge_request, :simple, source_project: project) } + let!(:mentioned_mr) { create(:merge_request, :simple, source_project: project) } let(:mentioned_commit) { project.commit } let(:ext_proj) { create(:project, :public) } diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 8dc687c3580..3eab74ba986 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -57,6 +57,10 @@ module TestEnv and_call_original end + def disable_pre_receive + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(true) + end + # Clean /tmp/tests # # Keeps gitlab-shell and gitlab-test |