diff options
93 files changed, 963 insertions, 381 deletions
diff --git a/CHANGELOG b/CHANGELOG index 0878c03207b..eab4ff4e6ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,13 @@ Please view this file on the master branch, on stable branches it's out of date. v 7.10.0 (unreleased) + - Fix broken file browsing with a submodule that contains a relative link (Stan Hu) + - Fix persistent XSS vulnerability around profile website URLs. + - Fix project import URL regex to prevent arbitary local repos from being imported. - Fix bug where Wiki pages that included a '/' were no longer accessible (Stan Hu) - Fix bug where error messages from Dropzone would not be displayed on the issues page (Stan Hu) - Add ability to configure Reply-To address in gitlab.yml (Stan Hu) + - Move current user to the top of the list in assignee/author filters (Stan Hu) - Fix broken side-by-side diff view on merge request page (Stan Hu) - Set Application controller default URL options to ensure all url_for calls are consistent (Stan Hu) - Allow HTML tags in Markdown input @@ -64,10 +68,20 @@ v 7.10.0 (unreleased) - Fixed link paths for HTTP and SSH on the admin project view (Jeremy Maziarz) - Fix and improve help rendering (Sullivan Sénéchal) - Fix final line in EmailsOnPush email diff being rendered as error. + - Authometic setup GitLab CI project for forks if origin project has GitLab CI enabled + - Prevent duplicate Buildkite service creation. + - Fix git over ssh errors 'fatal: protocol error: bad line length character' + - Automatically setup GitLab CI project for forks if origin project has GitLab CI enabled + - Bust group page project list cache when namespace name or path changes. v 7.9.3 - Contains no changes - Add icons to Add dropdown items. + - Allow admin to create public deploy keys that are accessible to any project. + - Warn when gitlab-shell version doesn't match requirement. + - Skip email confirmation when set by admin or via LDAP. + + - Only allow users to reference groups, projects, issues, MRs, commits they have access to. v 7.9.2 - Contains no changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42b5ce22e32..3165b7379d3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,7 +71,7 @@ If you can, please submit a merge request with the fix or improvements including 1. Fork the project on GitLab Cloud 1. Create a feature branch -1. Write [tests](README.md#run-the-tests) and code +1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code 1. Add your changes to the [CHANGELOG](CHANGELOG) 1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message 1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index e70b4523ae7..097a15a2af3 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.0 +2.6.2 @@ -208,7 +208,6 @@ group :development do gem "letter_opener" gem 'quiet_assets', '~> 1.0.1' gem 'rack-mini-profiler', require: false - gem "byebug" # Better errors handler gem 'better_errors' @@ -257,6 +256,8 @@ group :development, :test do gem "spring", '~> 1.3.1' gem "spring-commands-rspec", '1.0.4' gem "spring-commands-spinach", '1.0.0' + + gem "byebug" end group :test do diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index f464067686e..ccd85f2455d 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -8,6 +8,7 @@ class @UsersSelect @groupId = $(select).data('group-id') showNullUser = $(select).data('null-user') showAnyUser = $(select).data('any-user') + firstUser = $(select).data('first-user') $(select).select2 placeholder: "Search for a user" @@ -32,6 +33,13 @@ class @UsersSelect id: 0 } + if firstUser + # Move current user to the front of the list + for obj, index in data.results + if obj.username == firstUser + data.results.splice(index, 1) + data.results.unshift(obj) + break if showNullUser data.results.unshift(nullUser) if showAnyUser diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 83daea10ed5..d4af7506d5b 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -45,7 +45,8 @@ padding: 12px 0px; border-bottom: 1px solid #eee; .event-title { - @include str-truncated(72%); + max-width: 70%; + @include str-truncated(calc(100% - 174px)); font-weight: 500; font-size: 14px; .author_name { diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb new file mode 100644 index 00000000000..e93603bef36 --- /dev/null +++ b/app/controllers/admin/deploy_keys_controller.rb @@ -0,0 +1,49 @@ +class Admin::DeployKeysController < Admin::ApplicationController + before_filter :deploy_keys, only: [:index] + before_filter :deploy_key, only: [:show, :destroy] + + def index + + end + + def show + + end + + def new + @deploy_key = deploy_keys.new + end + + def create + @deploy_key = deploy_keys.new(deploy_key_params) + + if @deploy_key.save + redirect_to admin_deploy_keys_path + else + render "new" + end + end + + def destroy + deploy_key.destroy + + respond_to do |format| + format.html { redirect_to admin_deploy_keys_path } + format.json { head :ok } + end + end + + protected + + def deploy_key + @deploy_key ||= deploy_keys.find(params[:id]) + end + + def deploy_keys + @deploy_keys ||= DeployKey.are_public + end + + def deploy_key_params + params.require(:deploy_key).permit(:key, :title) + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 693970e5349..b4c011f213c 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -72,8 +72,8 @@ class Admin::UsersController < Admin::ApplicationController end respond_to do |format| + user.skip_reconfirmation! if user.update_attributes(user_params_with_pass) - user.confirm! format.html { redirect_to [:admin, user], notice: 'User was successfully updated.' } format.json { head :ok } else diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 80e983b5314..0521a9ef8cf 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -153,7 +153,7 @@ class ApplicationController < ActionController::Base end def method_missing(method_sym, *arguments, &block) - if method_sym.to_s =~ /^authorize_(.*)!$/ + if method_sym.to_s =~ /\Aauthorize_(.*)!\z/ authorize_project!($1.to_sym) else super diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 679a5d76ec0..6fba3ce299b 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -8,7 +8,14 @@ class Projects::DeployKeysController < Projects::ApplicationController def index @enabled_keys = @project.deploy_keys - @available_keys = available_keys - @enabled_keys + + @available_keys = accessible_keys - @enabled_keys + @available_project_keys = current_user.project_deploy_keys - @enabled_keys + @available_public_keys = DeployKey.are_public - @enabled_keys + + # Public keys that are already used by another accessible project are already + # in @available_project_keys. + @available_public_keys -= @available_project_keys end def show @@ -32,18 +39,9 @@ class Projects::DeployKeysController < Projects::ApplicationController end end - def destroy - @key = @project.deploy_keys.find(params[:id]) - @key.destroy - - respond_to do |format| - format.html { redirect_to namespace_project_deploy_keys_path(@project.namespace, @project) } - format.js { render nothing: true } - end - end - def enable - @project.deploy_keys << available_keys.find(params[:id]) + @key = accessible_keys.find(params[:id]) + @project.deploy_keys << @key redirect_to namespace_project_deploy_keys_path(@project.namespace, @project) @@ -52,14 +50,13 @@ class Projects::DeployKeysController < Projects::ApplicationController def disable @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy - redirect_to namespace_project_deploy_keys_path(@project.namespace, - @project) + redirect_to :back end protected - def available_keys - @available_keys ||= current_user.accessible_deploy_keys + def accessible_keys + @accessible_keys ||= current_user.accessible_deploy_keys end def deploy_key_params diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 7b6982c5074..3f11d7afe6f 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -26,6 +26,12 @@ class SessionsController < Devise::SessionsController end def create - super + super do |resource| + # User has successfully signed in, so clear any unused reset tokens + if resource.reset_password_token.present? + resource.update_attributes(reset_password_token: nil, + reset_password_sent_at: nil) + end + end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3f3509bb18a..b5b0015542c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -125,7 +125,7 @@ module ApplicationHelper # If reference is commit id - we should add it to branch/tag selectbox if(@ref && !options.flatten.include?(@ref) && - @ref =~ /^[0-9a-zA-Z]{6,52}$/) + @ref =~ /\A[0-9a-zA-Z]{6,52}\z/) options << ['Commit', [@ref]] end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 5aae697e2f0..d13d80be293 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -134,12 +134,13 @@ module CommitsHelper # avatar: true will prepend the avatar image # size: size of the avatar image in px def commit_person_link(commit, options = {}) + user = commit.send(options[:source]) + source_name = clean(commit.send "#{options[:source]}_name".to_sym) source_email = clean(commit.send "#{options[:source]}_email".to_sym) - user = User.find_for_commit(source_email, source_name) - person_name = user.nil? ? source_name : user.name - person_email = user.nil? ? source_email : user.email + person_name = user.try(:name) || source_name + person_email = user.try(:email) || source_email text = if options[:avatar] diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 17266656a4e..aa1de2f50ef 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -13,7 +13,7 @@ module GitlabMarkdownHelper def link_to_gfm(body, url, html_options = {}) return "" if body.blank? - escaped_body = if body =~ /^\<img/ + escaped_body = if body =~ /\A\<img/ body else escape_once(body) @@ -139,7 +139,7 @@ module GitlabMarkdownHelper @project.path_with_namespace, path_with_ref(file_path), file_path - ].compact.join("/").gsub(/^\/*|\/*$/, '') + id + ].compact.join("/").gsub(/\A\/*|\/*\z/, '') + id end def sanitize_slashes(path) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index e3734023be3..ebbd2bfd77d 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -127,6 +127,14 @@ module ProjectsHelper html + count_html end + def project_for_deploy_key(deploy_key) + if deploy_key.projects.include?(@project) + @project + else + deploy_key.projects.find { |project| can?(current_user, :read_project, project) } + end + end + private def get_project_nav_tabs(project, current_user) diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 457cd3fa46b..54e0f4f9b3e 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -8,12 +8,14 @@ module SelectsHelper null_user = opts[:null_user] || false any_user = opts[:any_user] || false + first_user = opts[:first_user] && current_user ? current_user.username : false html = { class: css_class, 'data-placeholder' => placeholder, 'data-null-user' => null_user, 'data-any-user' => any_user, + 'data-first-user' => first_user } unless opts[:scope] == :all diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index 241462e5e4c..9954617c762 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -44,7 +44,7 @@ module SubmoduleHelper def relative_self_url?(url) # (./)?(../repo.git) || (./)?(../../project/repo.git) ) - url =~ /^((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\Z/ || url =~ /^((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\Z/ + url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\z/ end def standard_links(host, namespace, project, commit) @@ -53,15 +53,22 @@ module SubmoduleHelper end def relative_self_links(url, commit) - if url.scan(/(\.\.\/)/).size == 2 - base = url[/([^\/]*\/[^\/]*)\.git/, 1] - else - base = [ @project.group.path, '/', url[/([^\/]*)\.git/, 1] ].join('') + # Map relative links to a namespace and project + # For example: + # ../bar.git -> same namespace, repo bar + # ../foo/bar.git -> namespace foo, repo bar + # ../../foo/bar/baz.git -> namespace bar, repo baz + components = url.split('/') + base = components.pop.gsub(/.git$/, '') + namespace = components.pop.gsub(/^\.\.$/, '') + + if namespace.empty? + namespace = @project.group.path end [ - namespace_project_path(base.namespace, base), - namespace_project_tree_path(base.namespace, base, commit) + namespace_project_path(namespace, base), + namespace_project_tree_path(namespace, base, commit) ] end end diff --git a/app/mailers/emails/groups.rb b/app/mailers/emails/groups.rb index 26f43bf955e..626eb593d51 100644 --- a/app/mailers/emails/groups.rb +++ b/app/mailers/emails/groups.rb @@ -4,6 +4,7 @@ module Emails @group_member = GroupMember.find(group_member_id) @group = @group_member.group @target_url = group_url(@group) + @current_user = @group_member.user mail(to: @group_member.user.email, subject: subject("Access to group was granted")) end diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb index ab5b0765352..3a83b083109 100644 --- a/app/mailers/emails/profile.rb +++ b/app/mailers/emails/profile.rb @@ -1,7 +1,7 @@ module Emails module Profile def new_user_email(user_id, token = nil) - @user = User.find(user_id) + @current_user = @user = User.find(user_id) @target_url = user_url(@user) @token = token mail(to: @user.notification_email, subject: subject("Account was created for you")) @@ -9,13 +9,13 @@ module Emails def new_email_email(email_id) @email = Email.find(email_id) - @user = @email.user + @current_user = @user = @email.user mail(to: @user.notification_email, subject: subject("Email was added to your account")) end def new_ssh_key_email(key_id) @key = Key.find(key_id) - @user = @key.user + @current_user = @user = @key.user @target_url = user_url(@user) mail(to: @user.notification_email, subject: subject("SSH key was added to your account")) end diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 3cd812825e2..20a863c3742 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -4,12 +4,13 @@ module Emails @project_member = ProjectMember.find user_project_id @project = @project_member.project @target_url = namespace_project_url(@project.namespace, @project) + @current_user = @project_member.user mail(to: @project_member.user.email, subject: subject("Access to project was granted")) end def project_was_moved_email(project_id, user_id) - @user = User.find user_id + @current_user = @user = User.find user_id @project = Project.find project_id @target_url = namespace_project_url(@project.namespace, @project) mail(to: @user.notification_email, @@ -28,7 +29,7 @@ module Emails end @project = Project.find(project_id) - @author = User.find(author_id) + @current_user = @author = User.find(author_id) @reverse_compare = reverse_compare @compare = compare @ref_name = Gitlab::Git.ref_name(ref) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 0c186ab5866..7c8b37029d1 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -13,6 +13,9 @@ class Notify < ActionMailer::Base add_template_helper MergeRequestsHelper add_template_helper EmailsHelper + attr_accessor :current_user + helper_method :current_user, :can? + default_url_options[:host] = Gitlab.config.gitlab.host default_url_options[:protocol] = Gitlab.config.gitlab.protocol default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port? @@ -79,9 +82,8 @@ class Notify < ActionMailer::Base # # Returns a String containing the User's email address. def recipient(recipient_id) - if recipient = User.find(recipient_id) - recipient.notification_email - end + @current_user = User.find(recipient_id) + @current_user.notification_email end # Set the References header field @@ -154,4 +156,8 @@ class Notify < ActionMailer::Base mail(headers, &block) end + + def can? + Ability.abilities.allowed?(user, action, subject) + end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 6e98c4c2f02..0d8365c4ff2 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -24,7 +24,7 @@ class ApplicationSetting < ActiveRecord::Base validates :home_page_url, allow_blank: true, - format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }, + format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :home_page_url_column_exist validates_each :restricted_visibility_levels do |record, attr, value| diff --git a/app/models/commit.rb b/app/models/commit.rb index e0461809e10..7a0ad137650 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -117,8 +117,8 @@ class Commit # Discover issues should be closed when this commit is pushed to a project's # default branch. - def closes_issues(project) - Gitlab::ClosingIssueExtractor.closed_by_message_in_project(safe_message, project) + def closes_issues(project, current_user = self.committer) + Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message) end # Mentionable override. @@ -126,6 +126,14 @@ class Commit "commit #{id}" end + def author + User.find_for_commit(author_email, author_name) + end + + def committer + User.find_for_commit(committer_email, committer_name) + end + def method_missing(m, *args, &block) @raw.send(m, *args, &block) end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 88ac83744df..478134dff68 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -118,16 +118,16 @@ module Issuable end # Return all users participating on the discussion - def participants + def participants(current_user = self.author) users = [] users << author users << assignee if is_assigned? mentions = [] - mentions << self.mentioned_users + mentions << self.mentioned_users(current_user) notes.each do |note| users << note.author - mentions << note.mentioned_users + mentions << note.mentioned_users(current_user) end users.concat(mentions.reduce([], :|)).uniq @@ -140,7 +140,7 @@ module Issuable return subscription.subscribed end - participants.include?(user) + participants(user).include?(user) end def toggle_subscription(user) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index d96e07034ec..b7882a2bb16 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -42,35 +42,22 @@ module Mentionable Note.cross_reference_exists?(target, local_reference) end - def mentioned_users - users = [] - return users if mentionable_text.blank? - has_project = self.respond_to? :project - matches = mentionable_text.scan(/@[a-zA-Z][a-zA-Z0-9_\-\.]*/) - matches.each do |match| - identifier = match.delete "@" - if identifier == "all" - users.push(*project.team.members.flatten) - elsif namespace = Namespace.find_by(path: identifier) - if namespace.is_a?(Group) - users.push(*namespace.users) - else - users << namespace.owner - end - end - end - users.uniq + def mentioned_users(current_user = nil) + return [] if mentionable_text.blank? + + ext = Gitlab::ReferenceExtractor.new(self.project, current_user) + ext.analyze(mentionable_text) + ext.users.uniq end # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. - def references(p = project, text = mentionable_text) + def references(p = project, current_user = self.author, text = mentionable_text) return [] if text.blank? - ext = Gitlab::ReferenceExtractor.new - ext.analyze(text, p) - (ext.issues_for(p) + - ext.merge_requests_for(p) + - ext.commits_for(p)).uniq - [local_reference] + ext = Gitlab::ReferenceExtractor.new(p, current_user) + ext.analyze(text) + + (ext.issues + ext.merge_requests + ext.commits).uniq - [local_reference] end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. @@ -96,7 +83,7 @@ module Mentionable # Only proceed if the saved changes actually include a chance to an attr_mentionable field. return unless mentionable_changed - preexisting = references(p, original) + preexisting = references(p, self.author, original) create_cross_references!(p, a, preexisting) end end diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 570f5e91c13..85d52d558cd 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -7,6 +7,7 @@ # created_at :datetime # updated_at :datetime # key :text +# public :boolean default(FALSE) # title :string(255) # type :string(255) # fingerprint :string(255) @@ -17,4 +18,21 @@ class DeployKey < Key has_many :projects, through: :deploy_keys_projects scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) } + scope :are_public, -> { where(public: true) } + + def private? + !public? + end + + def orphaned? + self.deploy_keys_projects.length == 0 + end + + def almost_orphaned? + self.deploy_keys_projects.length == 1 + end + + def destroyed_when_orphaned? + self.private? + end end diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb index 7e88903b9af..18db521741f 100644 --- a/app/models/deploy_keys_project.rb +++ b/app/models/deploy_keys_project.rb @@ -22,6 +22,8 @@ class DeployKeysProject < ActiveRecord::Base private def destroy_orphaned_deploy_key - self.deploy_key.destroy if self.deploy_key.deploy_keys_projects.length == 0 + return unless self.deploy_key.destroyed_when_orphaned? && self.deploy_key.orphaned? + + self.deploy_key.destroy end end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index defef7216f2..315d96af1b9 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -28,7 +28,7 @@ class WebHook < ActiveRecord::Base default_timeout Gitlab.config.gitlab.webhook_timeout validates :url, presence: true, - format: { with: URI::regexp(%w(http https)), message: "should be a valid url" } + format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" } def execute(data) parsed_url = URI.parse(url) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 5634f9a686e..35cb920d8bc 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -257,11 +257,11 @@ class MergeRequest < ActiveRecord::Base end # Return the set of issues that will be closed if this merge request is accepted. - def closes_issues + def closes_issues(current_user = self.author) if target_branch == project.default_branch - issues = commits.flat_map { |c| c.closes_issues(project) } - issues.push(*Gitlab::ClosingIssueExtractor. - closed_by_message_in_project(description, project)) + issues = commits.flat_map { |c| c.closes_issues(project, current_user) } + issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user). + closed_by_message(description)) issues.uniq.sort_by(&:id) else [] diff --git a/app/models/namespace.rb b/app/models/namespace.rb index dd74165f887..a0d79d7e5c0 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -61,8 +61,8 @@ class Namespace < ActiveRecord::Base def clean_path(path) path.gsub!(/@.*\z/, "") path.gsub!(/\.git\z/, "") - path.gsub!(/\A-/, "") - path.gsub!(/.\z/, "") + path.gsub!(/\A-+/, "") + path.gsub!(/\.+\z/, "") path.gsub!(/[^a-zA-Z0-9_\-\.]/, "") counter = 0 diff --git a/app/models/project.rb b/app/models/project.rb index 79572f255db..dcbafd76475 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -81,7 +81,7 @@ class Project < ActiveRecord::Base has_one :asana_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy has_one :slack_service, dependent: :destroy - has_one :buildbox_service, dependent: :destroy + has_one :buildkite_service, dependent: :destroy has_one :bamboo_service, dependent: :destroy has_one :teamcity_service, dependent: :destroy has_one :pushover_service, dependent: :destroy @@ -137,7 +137,7 @@ class Project < ActiveRecord::Base validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id validates :import_url, - format: { with: URI::regexp(%w(ssh git http https)), message: 'should be a valid url' }, + format: { with: /\A#{URI.regexp(%w(ssh git http https))}\z/, message: 'should be a valid url' }, if: :import? validates :star_count, numericality: { greater_than_or_equal_to: 0 } validate :check_limit, on: :create diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index f968afe9fa8..d8aedbd2ab4 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -25,7 +25,7 @@ class BambooService < CiService validates :bamboo_url, presence: true, - format: { with: URI::regexp }, + format: { with: /\A#{URI.regexp}\z/ }, if: :activated? validates :build_key, presence: true, if: :activated? validates :username, diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildkite_service.rb index 3a381ff11b8..a714bc82246 100644 --- a/app/models/project_services/buildbox_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -20,9 +20,7 @@ require "addressable/uri" -# Buildbox renamed to Buildkite, but for backwards compatability with the STI -# of Services, the class name is kept as "Buildbox" -class BuildboxService < CiService +class BuildkiteService < CiService ENDPOINT = "https://buildkite.com" prop_accessor :project_url, :token diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index e521186798c..a199d0e86f2 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -18,7 +18,7 @@ class ExternalWikiService < Service prop_accessor :external_wiki_url validates :external_wiki_url, presence: true, - format: { with: URI::regexp }, + format: { with: /\A#{URI.regexp}\z/ }, if: :activated? def title diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index edaeeffc228..0f9838a575d 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -18,6 +18,8 @@ # class GitlabCiService < CiService + API_PREFIX = "api/v1" + prop_accessor :project_url, :token validates :project_url, presence: true, if: :activated? validates :token, presence: true, if: :activated? @@ -59,6 +61,26 @@ class GitlabCiService < CiService end end + def fork_registration(new_project, private_token) + params = { + id: new_project.id, + name_with_namespace: new_project.name_with_namespace, + web_url: new_project.web_url, + default_branch: new_project.default_branch, + ssh_url_to_repo: new_project.ssh_url_to_repo + } + + HTTParty.post( + fork_registration_path, + body: { + project_id: project.id, + project_token: token, + private_token: private_token, + data: params }, + verify: false + ) + end + def commit_coverage(sha, ref) response = get_ci_build(sha, ref) @@ -97,4 +119,10 @@ class GitlabCiService < CiService { type: 'text', name: 'project_url', placeholder: 'http://ci.gitlabhq.com/projects/3' } ] end + + private + + def fork_registration_path + project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks") + end end diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index 2bddb7b881c..e9e1e276e7d 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -148,7 +148,7 @@ class IrkerService < Service def consider_uri(uri) # Authorize both irc://domain.com/#chan and irc://domain.com/chan - if uri.is_a?(URI) && uri.scheme[/^ircs?$/] && !uri.path.nil? + if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil? # Do not authorize irc://domain.com/ if uri.fragment.nil? && uri.path.length > 1 uri.to_s diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index c26bc551352..3c002a1634b 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -25,7 +25,7 @@ class TeamcityService < CiService validates :teamcity_url, presence: true, - format: { with: URI::regexp }, if: :activated? + format: { with: /\A#{URI.regexp}\z/ }, if: :activated? validates :build_type, presence: true, if: :activated? validates :username, presence: true, diff --git a/app/models/repository.rb b/app/models/repository.rb index 72769498872..263a436d521 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -199,7 +199,7 @@ class Repository def changelog cache.fetch(:changelog) do tree(:head).blobs.find do |file| - file.name =~ /^(changelog|history)/i + file.name =~ /\A(changelog|history)/i end end end @@ -207,7 +207,7 @@ class Repository def license cache.fetch(:license) do tree(:head).blobs.find do |file| - file.name =~ /^license/i + file.name =~ /\Alicense/i end end end diff --git a/app/models/service.rb b/app/models/service.rb index f54ad19666b..393cf55a69f 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -122,25 +122,25 @@ class Service < ActiveRecord::Base def self.available_services_names %w( - gitlab_ci - campfire - hipchat - pivotaltracker - flowdock - assembla asana + assembla + bamboo + buildkite + campfire + custom_issue_tracker emails_on_push + external_wiki + flowdock gemnasium - slack - pushover - buildbox - bamboo - teamcity + gitlab_ci + hipchat + irker jira + pivotaltracker + pushover redmine - custom_issue_tracker - irker - external_wiki + slack + teamcity ) end diff --git a/app/models/user.rb b/app/models/user.rb index 515f29ea0ba..a40111e62dd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -414,8 +414,16 @@ class User < ActiveRecord::Base @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"]) end + def project_deploy_keys + DeployKey.in_projects(self.authorized_projects.pluck(:id)) + end + def accessible_deploy_keys - DeployKey.in_projects(self.authorized_projects.pluck(:id)).uniq + @accessible_deploy_keys ||= begin + key_ids = project_deploy_keys.pluck(:id) + key_ids.push(*DeployKey.are_public.pluck(:id)) + DeployKey.where(id: key_ids) + end end def created_by @@ -486,13 +494,13 @@ class User < ActiveRecord::Base end def full_website_url - return "http://#{website_url}" if website_url !~ /^https?:\/\// + return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\// website_url end def short_website_url - website_url.gsub(/https?:\/\//, '') + website_url.sub(/\Ahttps?:\/\//, '') end def all_ssh_keys diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 4115d689925..25f9e203246 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -13,9 +13,7 @@ class CreateTagService < BaseService return error('Tag already exists') end - if message - message.gsub!(/^\s+|\s+$/, '') - end + message.strip! if message repository.add_tag(tag_name, ref, message) new_tag = repository.find_tag(tag_name) diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 1f0b29dff5e..31e0167d247 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -70,7 +70,7 @@ class GitPushService # Close issues if these commits were pushed to the project's default branch and the commit message matches the # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to # a different branch. - issues_to_close = commit.closes_issues(project) + issues_to_close = commit.closes_issues(project, user) # Load commit author only if needed. # For push with 1k commits it prevents 900+ requests in database @@ -87,7 +87,7 @@ class GitPushService # Create cross-reference notes for any other references. Omit any issues that were referenced in an # issue-closing phrase, or have already been mentioned from this commit (probably from this commit # being pushed to a different branch). - refs = commit.references(project) - issues_to_close + refs = commit.references(project, user) - issues_to_close refs.reject! { |r| commit.has_mentioned?(r) } if refs.present? @@ -127,6 +127,6 @@ class GitPushService end def commit_user(commit) - User.find_for_commit(commit.author_email, commit.author_name) || user + commit.author || user end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index cc5853144c5..42547f6f481 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -123,32 +123,29 @@ class NotificationService return true if note.note.start_with?('Status changed to closed') return true if note.cross_reference? && note.system == true - opts = { noteable_type: note.noteable_type, project_id: note.project_id } - target = note.noteable - if target.respond_to?(:participants) - recipients = target.participants - else - recipients = note.mentioned_users - end + recipients = [] if note.commit_id.present? - opts.merge!(commit_id: note.commit_id) recipients << note.commit_author - else - opts.merge!(noteable_id: note.noteable_id) end # Get users who left comment in thread - recipients = recipients.concat(User.where(id: Note.where(opts).pluck(:author_id))) + recipients = recipients.concat(noteable_commenters(note)) # Merge project watchers recipients = recipients.concat(project_watchers(note.project)).compact.uniq - # Reject mention users unless mentioned in comment - recipients = reject_mention_users(recipients - note.mentioned_users, note.project) - recipients = recipients + note.mentioned_users + # Reject users with Mention notification level + recipients = reject_mention_users(recipients, note.project) + + # Add explicitly mentioned users + if target.respond_to?(:participants) + recipients = recipients.concat(target.participants) + else + recipients = recipients.concat(note.mentioned_users) + end # Reject mutes users recipients = reject_muted_users(recipients, note.project) @@ -195,6 +192,18 @@ class NotificationService protected + def noteable_commenters(note) + opts = { noteable_type: note.noteable_type, project_id: note.project_id } + + if note.commit_id.present? + opts.merge!(commit_id: note.commit_id) + else + opts.merge!(noteable_id: note.noteable_id) + end + + User.where(id: Note.where(opts).pluck(:author_id)) + end + # Get project users with WATCH notification level def project_watchers(project) project_members = project_member_notification(project) diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 6b0d4aca3e1..4ec98696a65 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -40,12 +40,18 @@ module Projects if project.save project.team << [@current_user, :master] end + #Now fork the repo unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path) raise 'forking failed in gitlab-shell' end + project.ensure_satellite_exists end + + if @from_project.gitlab_ci? + ForkRegistrationWorker.perform_async(@from_project.id, project.id, @current_user.private_token) + end rescue => ex project.errors.add(:base, 'Fork transaction failed.') project.destroy diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index bcbacbff562..ae6260bcdab 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -1,10 +1,5 @@ module Projects class ParticipantsService < BaseService - def initialize(project, user) - @project = project - @user = user - end - def execute(note_type, note_id) participating = if note_type && note_id @@ -12,7 +7,7 @@ module Projects else [] end - project_members = sorted(@project.team.members) + project_members = sorted(project.team.members) participants = all_members + groups + project_members + participating participants.uniq end @@ -20,11 +15,11 @@ module Projects def participants_in(type, id) users = case type when "Issue" - issue = @project.issues.find_by_iid(id) - issue ? issue.participants : [] + issue = project.issues.find_by_iid(id) + issue ? issue.participants(current_user) : [] when "MergeRequest" - merge_request = @project.merge_requests.find_by_iid(id) - merge_request ? merge_request.participants : [] + merge_request = project.merge_requests.find_by_iid(id) + merge_request ? merge_request.participants(current_user) : [] when "Commit" author_ids = Note.for_commit_id(id).pluck(:author_id).uniq User.where(id: author_ids) @@ -41,14 +36,14 @@ module Projects end def groups - @user.authorized_groups.sort_by(&:path).map do |group| + current_user.authorized_groups.sort_by(&:path).map do |group| count = group.users.count { username: group.path, name: "#{group.name} (#{count})" } end end def all_members - count = @project.team.members.flatten.count + count = project.team.members.flatten.count [{ username: "all", name: "All Project and Group Members (#{count})" }] end end diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml new file mode 100644 index 00000000000..2ae83ab95f7 --- /dev/null +++ b/app/views/admin/deploy_keys/index.html.haml @@ -0,0 +1,27 @@ +.panel.panel-default + .panel-heading + Public deploy keys (#{@deploy_keys.count}) + .panel-head-actions + = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm" + - if @deploy_keys.any? + %table.table + %thead.panel-heading + %tr + %th Title + %th Fingerprint + %th Added at + %th + %tbody + - @deploy_keys.each do |deploy_key| + %tr + %td + = link_to admin_deploy_key_path(deploy_key) do + %strong= deploy_key.title + %td + %span + (#{deploy_key.fingerprint}) + %td + %span.cgray + added #{time_ago_with_tooltip(deploy_key.created_at)} + %td + = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right" diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml new file mode 100644 index 00000000000..c00049424c5 --- /dev/null +++ b/app/views/admin/deploy_keys/new.html.haml @@ -0,0 +1,26 @@ +%h3.page-title New public deploy key +%hr + +%div + = form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f| + -if @deploy_key.errors.any? + .alert.alert-danger + %ul + - @deploy_key.errors.full_messages.each do |msg| + %li= msg + + .form-group + = f.label :title, class: "control-label" + .col-sm-10= f.text_field :title, class: 'form-control' + .form-group + = f.label :key, class: "control-label" + .col-sm-10 + %p.light + Paste a machine public key here. Read more about how to generate it + = link_to "here", help_page_path("ssh", "README") + = f.text_area :key, class: "form-control thin_area", rows: 5 + + .form-actions + = f.submit 'Create', class: "btn-create btn" + = link_to "Cancel", admin_deploy_keys_path, class: "btn btn-cancel" + diff --git a/app/views/admin/deploy_keys/show.html.haml b/app/views/admin/deploy_keys/show.html.haml new file mode 100644 index 00000000000..cfa2adf92ee --- /dev/null +++ b/app/views/admin/deploy_keys/show.html.haml @@ -0,0 +1,34 @@ +.row + .col-md-4 + .panel.panel-default + .panel-heading + Deploy Key + %ul.well-list + %li + %span.light Title: + %strong= @deploy_key.title + %li + %span.light Created on: + %strong= @deploy_key.created_at.stamp("Aug 21, 2011") + + .panel.panel-default + .panel-heading Projects (#{@deploy_key.deploy_keys_projects.count}) + - if @deploy_key.deploy_keys_projects.any? + %ul.well-list + - @deploy_key.projects.each do |project| + %li + %span + %strong + = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project] + .pull-right + = link_to disable_namespace_project_deploy_key_path(project.namespace, project, @deploy_key), data: { confirm: "Are you sure?" }, method: :put, class: "btn-xs btn btn-remove", title: 'Remove deploy key from project' do + %i.fa.fa-times.fa-inverse + + .col-md-8 + %p + %span.light Fingerprint: + %strong= @deploy_key.fingerprint + %pre.well-pre + = @deploy_key.key + .pull-right + = link_to 'Remove', admin_deploy_key_path(@deploy_key), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key" diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index 670f5ac7af7..f4ad2b294b3 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -19,5 +19,5 @@ %i.fa.fa-angle-left - else - %h3 You dont have starred projects yet + %h3 You don't have starred projects yet %p.slead Visit project page and press on star icon and it will appear on this page. diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 2f38d596c65..34efceb37d1 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -19,6 +19,11 @@ %i.fa.fa-group %span Groups + = nav_link(controller: :deploy_keys) do + = link_to admin_deploy_keys_path, title: 'Deploy Keys' do + %i.fa.fa-key + %span + Deploy Keys = nav_link(controller: :logs) do = link_to admin_logs_path, title: 'Logs' do %i.fa.fa-file-text diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml index a2faa9d5e25..c577dfa8d55 100644 --- a/app/views/projects/deploy_keys/_deploy_key.html.haml +++ b/app/views/projects/deploy_keys/_deploy_key.html.haml @@ -5,21 +5,32 @@ %i.fa.fa-plus Enable - else - - if deploy_key.projects.count > 1 + - if deploy_key.destroyed_when_orphaned? && deploy_key.almost_orphaned? + = link_to 'Remove', disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :put, class: "btn btn-remove delete-key btn-sm pull-right" + - else = link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-sm', method: :put do %i.fa.fa-power-off Disable - - else - = link_to 'Remove', namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :delete, class: "btn btn-remove delete-key btn-sm pull-right" - - - key_project = deploy_key.projects.include?(@project) ? @project : deploy_key.projects.first - = link_to namespace_project_deploy_key_path(key_project.namespace, key_project, deploy_key) do + - if project = project_for_deploy_key(deploy_key) + = link_to namespace_project_deploy_key_path(project.namespace, project, deploy_key) do + %i.fa.fa-key + %strong= deploy_key.title + - else %i.fa.fa-key %strong= deploy_key.title + %p.light.prepend-top-10 - - deploy_key.projects.map(&:name_with_namespace).each do |project_name| - %span.label.label-gray.deploy-project-label= project_name + - if deploy_key.public? + %span.label.label-info.deploy-project-label + Public deploy key + + - deploy_key.projects.each do |project| + - if can?(current_user, :read_project, project) + %span.label.label-gray.deploy-project-label + = link_to namespace_project_path(project.namespace, project) do + = project.name_with_namespace + %small.pull-right Created #{time_ago_with_tooltip(deploy_key.created_at)} diff --git a/app/views/projects/deploy_keys/index.html.haml b/app/views/projects/deploy_keys/index.html.haml index c02a18146eb..472a13a8524 100644 --- a/app/views/projects/deploy_keys/index.html.haml +++ b/app/views/projects/deploy_keys/index.html.haml @@ -22,11 +22,20 @@ .light-well .nothing-here-block Create a #{link_to 'new deploy key', new_namespace_project_deploy_key_path(@project.namespace, @project)} or add an existing one .col-md-6.available-keys - %h5 - %strong Deploy keys - from projects available to you - %ul.bordered-list - = render @available_keys - - if @available_keys.blank? - .light-well - .nothing-here-block Deploy keys from projects you have access to will be displayed here + - # If there are available public deploy keys but no available project deploy keys, only public deploy keys are shown. + - if @available_project_keys.any? || @available_public_keys.blank? + %h5 + %strong Deploy keys + from projects you have access to + %ul.bordered-list + = render @available_project_keys + - if @available_project_keys.blank? + .light-well + .nothing-here-block Deploy keys from projects you have access to will be displayed here + + - if @available_public_keys.any? + %h5 + %strong Public deploy keys + available to any project + %ul.bordered-list + = render @available_public_keys diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 2b9b6599a7d..b49aee504fe 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -20,4 +20,4 @@ Maybe diff is really big and operation failed with timeout. Try to get diff locally :coffeescript - $('.files .diff-header').stick_in_parent(recalc_every: 1, offset_top: $('.navbar').height()) + $('.files .diff-header').stick_in_parent(offset_top: $('.navbar').height()) diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 0d3028d50b4..288b48f4583 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -9,8 +9,8 @@ .votes-holder.pull-right #votes= render 'votes/votes_block', votable: @issue .participants - %span= pluralize(@issue.participants.count, 'participant') - - @issue.participants.each do |participant| + %span= pluralize(@issue.participants(current_user).count, 'participant') + - @issue.participants(current_user).each do |participant| = link_to_member(@project, participant, name: false, size: 24) .voting_notes#notes= render "projects/notes/notes_with_form" %aside.col-md-3 diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index 52e38050419..9228074d833 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -8,7 +8,7 @@ - else none - if can?(current_user, :modify_issue, @issue) - = users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id, null_user: true) + = users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id, null_user: true, first_user: true) %div.prepend-top-20.clearfix .issuable-context-title diff --git a/app/views/projects/merge_requests/show/_participants.html.haml b/app/views/projects/merge_requests/show/_participants.html.haml index 4f34af1737d..9c93fa55fe6 100644 --- a/app/views/projects/merge_requests/show/_participants.html.haml +++ b/app/views/projects/merge_requests/show/_participants.html.haml @@ -1,4 +1,4 @@ .participants - %span #{@merge_request.participants.count} participants - - @merge_request.participants.each do |participant| + %span #{@merge_request.participants(current_user).count} participants + - @merge_request.participants(current_user).each do |participant| = link_to_member(@project, participant, name: false, size: 24) diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml index f169733f2e9..83f5a3a8015 100644 --- a/app/views/shared/_issuable_filter.html.haml +++ b/app/views/shared/_issuable_filter.html.haml @@ -24,11 +24,11 @@ .issues-other-filters .filter-item.inline = users_select_tag(:assignee_id, selected: params[:assignee_id], - placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true) + placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true, first_user: true) .filter-item.inline = users_select_tag(:author_id, selected: params[:author_id], - placeholder: 'Author', class: 'trigger-submit', any_user: true) + placeholder: 'Author', class: 'trigger-submit', any_user: true, first_user: true) .filter-item.inline.milestone-filter = select_tag('milestone_id', projects_milestones_options, class: "select2 trigger-submit", prompt: 'Milestone') diff --git a/app/views/shared/_project.html.haml b/app/views/shared/_project.html.haml index 8746970c239..722a7f7ce0f 100644 --- a/app/views/shared/_project.html.haml +++ b/app/views/shared/_project.html.haml @@ -1,4 +1,4 @@ -= cache [project, controller.controller_name, controller.action_name] do += cache [project.namespace, project, controller.controller_name, controller.action_name] do = link_to project_path(project), class: dom_class(project) do - if avatar .dash-project-avatar diff --git a/app/workers/fork_registration_worker.rb b/app/workers/fork_registration_worker.rb new file mode 100644 index 00000000000..fffa8b3a659 --- /dev/null +++ b/app/workers/fork_registration_worker.rb @@ -0,0 +1,12 @@ +class ForkRegistrationWorker + include Sidekiq::Worker + + sidekiq_options queue: :default + + def perform(from_project_id, to_project_id, private_token) + from_project = Project.find(from_project_id) + to_project = Project.find(to_project_id) + + from_project.gitlab_ci_service.fork_registration(to_project, private_token) + end +end diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 0c3ee6ba4ff..33d8cc8861b 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -11,8 +11,8 @@ class PostReceive log("Check gitlab.yml config for correct gitlab_shell.repos_path variable. \"#{Gitlab.config.gitlab_shell.repos_path}\" does not match \"#{repo_path}\"") end - repo_path.gsub!(/\.git$/, "") - repo_path.gsub!(/^\//, "") + repo_path.gsub!(/\.git\z/, "") + repo_path.gsub!(/\A\//, "") project = Project.find_with_namespace(repo_path) diff --git a/config/initializers/5_backend.rb b/config/initializers/5_backend.rb index 7c2e7f39000..80d641d73a3 100644 --- a/config/initializers/5_backend.rb +++ b/config/initializers/5_backend.rb @@ -6,3 +6,10 @@ require Rails.root.join("lib", "gitlab", "backend", "shell") # GitLab shell adapter require Rails.root.join("lib", "gitlab", "backend", "shell_adapter") + +required_version = Gitlab::VersionInfo.parse(Gitlab::Shell.version_required) +current_version = Gitlab::VersionInfo.parse(Gitlab::Shell.new.version) + +unless current_version.valid? && required_version <= current_version + warn "WARNING: This version of GitLab depends on gitlab-shell #{required_version}, but you're running #{current_version}. Please update gitlab-shell." +end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 79abe3c695d..9dce495106f 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -208,7 +208,7 @@ Devise.setup do |config| if Gitlab::LDAP::Config.enabled? Gitlab.config.ldap.servers.values.each do |server| if server['allow_username_or_email_login'] - email_stripping_proc = ->(name) {name.gsub(/@.*$/,'')} + email_stripping_proc = ->(name) {name.gsub(/@.*\z/,'')} else email_stripping_proc = ->(name) {name} end diff --git a/config/routes.rb b/config/routes.rb index c1b85b025b5..de21f418329 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -145,6 +145,8 @@ Gitlab::Application.routes.draw do end end + resources :deploy_keys, only: [:index, :show, :new, :create, :destroy] + resources :hooks, only: [:index, :create, :destroy] do get :test end @@ -393,7 +395,7 @@ Gitlab::Application.routes.draw do end end - resources :deploy_keys, constraints: { id: /\d+/ } do + resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :show, :new, :create] do member do put :enable put :disable diff --git a/db/migrate/20150327122227_add_public_to_key.rb b/db/migrate/20150327122227_add_public_to_key.rb new file mode 100644 index 00000000000..6ffbf4cda19 --- /dev/null +++ b/db/migrate/20150327122227_add_public_to_key.rb @@ -0,0 +1,5 @@ +class AddPublicToKey < ActiveRecord::Migration + def change + add_column :keys, :public, :boolean, default: false, null: false + end +end diff --git a/db/migrate/20150411180045_rename_buildbox_service.rb b/db/migrate/20150411180045_rename_buildbox_service.rb new file mode 100644 index 00000000000..5a0b5d07e50 --- /dev/null +++ b/db/migrate/20150411180045_rename_buildbox_service.rb @@ -0,0 +1,9 @@ +class RenameBuildboxService < ActiveRecord::Migration + def up + execute "UPDATE services SET type = 'BuildkiteService' WHERE type = 'BuildboxService';" + end + + def down + execute "UPDATE services SET type = 'BuildboxService' WHERE type = 'BuildkiteService';" + end +end diff --git a/db/schema.rb b/db/schema.rb index 903ed161e4a..48f1b2ac2cc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150411000035) do +ActiveRecord::Schema.define(version: 20150411180045) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -132,6 +132,7 @@ ActiveRecord::Schema.define(version: 20150411000035) do t.string "title" t.string "type" t.string "fingerprint" + t.boolean "public", default: false, null: false end add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree @@ -336,12 +337,12 @@ ActiveRecord::Schema.define(version: 20150411000035) do t.string "import_url" t.integer "visibility_level", default: 0, null: false t.boolean "archived", default: false, null: false + t.string "avatar" t.string "import_status" t.float "repository_size", default: 0.0 t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.string "avatar" end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree diff --git a/doc/api/projects.md b/doc/api/projects.md index 55d525fef66..971fe96fb8e 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -63,6 +63,7 @@ Parameters: "snippets_enabled": false, "created_at": "2013-09-30T13: 46: 02Z", "last_activity_at": "2013-09-30T13: 46: 02Z", + "creator_id": 3, "namespace": { "created_at": "2013-09-30T13: 46: 02Z", "description": "", @@ -103,6 +104,7 @@ Parameters: "snippets_enabled": false, "created_at": "2013-09-30T13:46:02Z", "last_activity_at": "2013-09-30T13:46:02Z", + "creator_id": 3, "namespace": { "created_at": "2013-09-30T13:46:02Z", "description": "", @@ -190,6 +192,7 @@ Parameters: "snippets_enabled": false, "created_at": "2013-09-30T13: 46: 02Z", "last_activity_at": "2013-09-30T13: 46: 02Z", + "creator_id": 3, "namespace": { "created_at": "2013-09-30T13: 46: 02Z", "description": "", diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index 965d8fc313f..1d5fd4c8b0d 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -421,7 +421,7 @@ Quote break. You can also use raw HTML in your Markdown, and it'll mostly work pretty well. -Note that inline HTML is disabled in the default Gitlab configuration, although it is [possible](https://github.com/gitlabhq/gitlabhq/pull/8007/commits) for the system administrator to enable it. +See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows the `class`, `id`, and `style` attributes. ```no-highlight <dl> @@ -441,8 +441,6 @@ Note that inline HTML is disabled in the default Gitlab configuration, although <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd> </dl> -See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows the `class`, `id`, and `style` attributes. - ## Horizontal Rule ``` diff --git a/features/admin/deploy_keys.feature b/features/admin/deploy_keys.feature new file mode 100644 index 00000000000..9df47eb51fd --- /dev/null +++ b/features/admin/deploy_keys.feature @@ -0,0 +1,21 @@ +@admin +Feature: Admin Deploy Keys + Background: + Given I sign in as an admin + And there are public deploy keys in system + + Scenario: Deploy Keys list + When I visit admin deploy keys page + Then I should see all public deploy keys + + Scenario: Deploy Keys show + When I visit admin deploy keys page + And I click on first deploy key + Then I should see deploy key details + + Scenario: Deploy Keys new + When I visit admin deploy keys page + And I click 'New Deploy Key' + And I submit new deploy key + Then I should be on admin deploy keys page + And I should see newly created deploy key diff --git a/features/project/deploy_keys.feature b/features/project/deploy_keys.feature index 13e3b9bbd2e..a71f6124d9c 100644 --- a/features/project/deploy_keys.feature +++ b/features/project/deploy_keys.feature @@ -6,7 +6,17 @@ Feature: Project Deploy Keys Scenario: I should see deploy keys list Given project has deploy key When I visit project deploy keys page - Then I should see project deploy keys + Then I should see project deploy key + + Scenario: I should see project deploy keys + Given other project has deploy key + When I visit project deploy keys page + Then I should see other project deploy key + + Scenario: I should see public deploy keys + Given public deploy key exists + When I visit project deploy keys page + Then I should see public deploy key Scenario: I add new deploy key Given I visit project deploy keys page @@ -15,9 +25,16 @@ Feature: Project Deploy Keys Then I should be on deploy keys page And I should see newly created deploy key - Scenario: I attach deploy key to project + Scenario: I attach other project deploy key to project Given other project has deploy key And I visit project deploy keys page When I click attach deploy key Then I should be on deploy keys page And I should see newly created deploy key + + Scenario: I attach public deploy key to project + Given public deploy key exists + And I visit project deploy keys page + When I click attach deploy key + Then I should be on deploy keys page + And I should see newly created deploy key diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature index af01c7058ea..eb813884d1e 100644 --- a/features/project/issues/issues.feature +++ b/features/project/issues/issues.feature @@ -25,6 +25,12 @@ Feature: Project Issues Given I click link "Release 0.4" Then I should see issue "Release 0.4" + @javascript + Scenario: I visit issue page + Given I add a user to project "Shop" + And I click "author" dropdown + Then I see current user as the first user + Scenario: I submit new unassigned issue Given I click link "New Issue" And I submit new issue "500 error on profile" diff --git a/features/steps/admin/deploy_keys.rb b/features/steps/admin/deploy_keys.rb new file mode 100644 index 00000000000..fb0b611762e --- /dev/null +++ b/features/steps/admin/deploy_keys.rb @@ -0,0 +1,57 @@ +class Spinach::Features::AdminDeployKeys < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedAdmin + + step 'there are public deploy keys in system' do + create(:deploy_key, public: true) + create(:another_deploy_key, public: true) + end + + step 'I should see all public deploy keys' do + DeployKey.are_public.each do |p| + page.should have_content p.title + end + end + + step 'I click on first deploy key' do + click_link DeployKey.are_public.first.title + end + + step 'I should see deploy key details' do + deploy_key = DeployKey.are_public.first + current_path.should == admin_deploy_key_path(deploy_key) + page.should have_content(deploy_key.title) + page.should have_content(deploy_key.key) + end + + step 'I visit admin deploy key page' do + visit admin_deploy_key_path(deploy_key) + end + + step 'I visit admin deploy keys page' do + visit admin_deploy_keys_path + end + + step 'I click \'New Deploy Key\'' do + click_link 'New Deploy Key' + end + + step 'I submit new deploy key' do + fill_in "deploy_key_title", with: "laptop" + fill_in "deploy_key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop" + click_button "Create" + end + + step 'I should be on admin deploy keys page' do + current_path.should == admin_deploy_keys_path + end + + step 'I should see newly created deploy key' do + page.should have_content(deploy_key.title) + end + + def deploy_key + @deploy_key ||= DeployKey.are_public.first + end +end diff --git a/features/steps/project/deploy_keys.rb b/features/steps/project/deploy_keys.rb index 4bf5cb5fa40..50e14513a7a 100644 --- a/features/steps/project/deploy_keys.rb +++ b/features/steps/project/deploy_keys.rb @@ -7,12 +7,24 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps create(:deploy_keys_project, project: @project) end - step 'I should see project deploy keys' do + step 'I should see project deploy key' do within '.enabled-keys' do page.should have_content deploy_key.title end end + step 'I should see other project deploy key' do + within '.available-keys' do + page.should have_content other_deploy_key.title + end + end + + step 'I should see public deploy key' do + within '.available-keys' do + page.should have_content public_deploy_key.title + end + end + step 'I click \'New Deploy Key\'' do click_link 'New Deploy Key' end @@ -39,6 +51,10 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps create(:deploy_keys_project, project: @second_project) end + step 'public deploy key exists' do + create(:deploy_key, public: true) + end + step 'I click attach deploy key' do within '.available-keys' do click_link 'Enable' @@ -50,4 +66,12 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps def deploy_key @project.deploy_keys.last end + + def other_deploy_key + @second_project.deploy_keys.last + end + + def public_deploy_key + DeployKey.are_public.last + end end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index e2834d51a27..b8e282b2029 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -59,6 +59,18 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps click_link "New Issue" end + step 'I click "author" dropdown' do + first('.ajax-users-select').click + end + + step 'I see current user as the first user' do + expect(page).to have_selector('.user-result', visible: true, count: 4) + users = page.all('.user-name') + users[0].text.should == 'Any' + users[1].text.should == 'Unassigned' + users[2].text.should == current_user.name + end + step 'I submit new issue "500 error on profile"' do fill_in "issue_title", with: "500 error on profile" click_button "Submit new issue" diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 41f71ae29cb..b60ac5e3423 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -14,6 +14,13 @@ module SharedProject @project.team << [@user, :master] end + # Add another user to project "Shop" + step 'I add a user to project "Shop"' do + @project = Project.find_by(name: "Shop") + other_user = create(:user, name: 'Alpha') + @project.team << [other_user, :master] + end + # Create another specific project called "Forum" step 'I own project "Forum"' do @project = Project.find_by(name: "Forum") diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 51cb934616b..36332bc6514 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -54,6 +54,7 @@ module API expose :name, :name_with_namespace expose :path, :path_with_namespace expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at + expose :creator_id expose :namespace expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? } expose :avatar_url diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index aabc7f1e69a..530f9d93de4 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -240,7 +240,7 @@ module Gitlab gitlab_shell_version_file = "#{gitlab_shell_path}/VERSION" if File.readable?(gitlab_shell_version_file) - File.read(gitlab_shell_version_file) + File.read(gitlab_shell_version_file).chomp end end diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index a9fd59f03d9..ab184d95c05 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -1,21 +1,20 @@ module Gitlab - module ClosingIssueExtractor + class ClosingIssueExtractor ISSUE_CLOSING_REGEX = Regexp.new(Gitlab.config.gitlab.issue_closing_pattern) - def self.closed_by_message_in_project(message, project) - issues = [] + def initialize(project, current_user = nil) + @extractor = Gitlab::ReferenceExtractor.new(project, current_user) + end - unless message.nil? - md = message.scan(ISSUE_CLOSING_REGEX) + def closed_by_message(message) + return [] if message.nil? + + closing_statements = message.scan(ISSUE_CLOSING_REGEX). + map { |ref| ref[0] }.join(" ") - md.each do |ref| - extractor = Gitlab::ReferenceExtractor.new - extractor.analyze(ref[0], project) - issues += extractor.issues_for(project) - end - end + @extractor.analyze(closing_statements) - issues.uniq + @extractor.issues end end end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index 75026aeaeb2..d054014039a 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -39,6 +39,7 @@ module Gitlab end def update_user_attributes + gl_user.skip_reconfirmation! gl_user.email = auth_hash.email # Build new identity only if we dont have have same one diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index f1e2ae74a3a..8073417a16a 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -192,6 +192,7 @@ module Gitlab project_path = $LAST_MATCH_INFO[:project] if project_path actual_project = ::Project.find_with_namespace(project_path) + actual_project = nil unless can?(current_user, :read_project, actual_project) project_prefix = project_path end @@ -251,6 +252,7 @@ module Gitlab elsif namespace = Namespace.find_by(path: identifier) url = if namespace.is_a?(Group) + return nil unless can?(current_user, :read_group, namespace) group_url(identifier, only_path: options[:reference_only_path]) else user_url(identifier, only_path: options[:reference_only_path]) diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index c0419585c4b..a502a8fe9cd 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -1,94 +1,94 @@ module Gitlab # Extract possible GFM references from an arbitrary String for further processing. class ReferenceExtractor - attr_accessor :users, :labels, :issues, :merge_requests, :snippets, :commits, :commit_ranges + attr_accessor :project, :current_user, :references include ::Gitlab::Markdown - def initialize - @users, @labels, @issues, @merge_requests, @snippets, @commits, @commit_ranges = - [], [], [], [], [], [], [] + def initialize(project, current_user = nil) + @project = project + @current_user = current_user end - def analyze(string, project) - text = string.dup + def can?(user, action, subject) + Ability.abilities.allowed?(user, action, subject) + end + + def analyze(text) + text = text.dup # Remove preformatted/code blocks so that references are not included text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) { |match| '' } text.gsub!(%r{^```.*?^```}m) { |match| '' } - parse_references(text, project) + @references = Hash.new { |hash, type| hash[type] = [] } + parse_references(text) end # Given a valid project, resolve the extracted identifiers of the requested type to # model objects. - def users_for(project) - users.map do |entry| - project.users.where(username: entry[:id]).first - end.reject(&:nil?) + def users + references[:user].uniq.map do |project, identifier| + if identifier == "all" + project.team.members.flatten + elsif namespace = Namespace.find_by(path: identifier) + if namespace.is_a?(Group) + namespace.users + else + namespace.owner + end + end + end.flatten.compact.uniq end - def labels_for(project = nil) - labels.map do |entry| - project.labels.where(id: entry[:id]).first - end.reject(&:nil?) + def labels + references[:label].uniq.map do |project, identifier| + project.labels.where(id: identifier).first + end.compact.uniq end - def issues_for(project = nil) - issues.map do |entry| - if should_lookup?(project, entry[:project]) - entry[:project].issues.where(iid: entry[:id]).first + def issues + references[:issue].uniq.map do |project, identifier| + if project.default_issues_tracker? + project.issues.where(iid: identifier).first end - end.reject(&:nil?) + end.compact.uniq end - def merge_requests_for(project = nil) - merge_requests.map do |entry| - if should_lookup?(project, entry[:project]) - entry[:project].merge_requests.where(iid: entry[:id]).first - end - end.reject(&:nil?) + def merge_requests + references[:merge_request].uniq.map do |project, identifier| + project.merge_requests.where(iid: identifier).first + end.compact.uniq end - def snippets_for(project) - snippets.map do |entry| - project.snippets.where(id: entry[:id]).first - end.reject(&:nil?) + def snippets + references[:snippet].uniq.map do |project, identifier| + project.snippets.where(id: identifier).first + end.compact.uniq end - def commits_for(project = nil) - commits.map do |entry| - repo = entry[:project].repository if entry[:project] - if should_lookup?(project, entry[:project]) - repo.commit(entry[:id]) if repo - end - end.reject(&:nil?) + def commits + references[:commit].uniq.map do |project, identifier| + repo = project.repository + repo.commit(identifier) if repo + end.compact.uniq end - def commit_ranges_for(project = nil) - commit_ranges.map do |entry| - repo = entry[:project].repository if entry[:project] - if repo && should_lookup?(project, entry[:project]) - from_id, to_id = entry[:id].split(/\.{2,3}/, 2) + def commit_ranges + references[:commit_range].uniq.map do |project, identifier| + repo = project.repository + if repo + from_id, to_id = identifier.split(/\.{2,3}/, 2) [repo.commit(from_id), repo.commit(to_id)] end - end.reject(&:nil?) + end.compact.uniq end private def reference_link(type, identifier, project, _) - # Append identifier to the appropriate collection. - send("#{type}s") << { project: project, id: identifier } - end - - def should_lookup?(project, entry_project) - if entry_project.nil? - false - else - project.nil? || entry_project.default_issues_tracker? - end + references[type] << [project, identifier] end end end diff --git a/spec/factories.rb b/spec/factories.rb index fc103e5b133..b9bfd3cebb4 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -111,6 +111,9 @@ FactoryGirl.define do key do "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmTillFzNTrrGgwaCKaSj+QCz81E6jBc/s9av0+3b1Hwfxgkqjl4nAK/OD2NjgyrONDTDfR8cRN4eAAy6nY8GLkOyYBDyuc5nTMqs5z3yVuTwf3koGm/YQQCmo91psZ2BgDFTor8SVEE5Mm1D1k3JDMhDFxzzrOtRYFPci9lskTJaBjpqWZ4E9rDTD2q/QZntCqbC3wE9uSemRQB5f8kik7vD/AD8VQXuzKladrZKkzkONCPWsXDspUitjM8HkQdOf0PsYn1CMUC1xKYbCxkg5TkEosIwGv6CoEArUrdu/4+10LVslq494mAvEItywzrluCLCnwELfW+h/m8UHoVhZ" end + + factory :another_deploy_key, class: 'DeployKey' do + end end factory :invalid_key do diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index 21a3a4bf937..4cfaab03caf 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -1,14 +1,37 @@ require 'spec_helper' -describe 'Users', feature: true do - describe "GET /users/sign_in" do - it "should create a new user account" do - visit new_user_session_path - fill_in "user_name", with: "Name Surname" - fill_in "user_username", with: "Great" - fill_in "user_email", with: "name@mail.com" - fill_in "user_password_sign_up", with: "password1234" - expect { click_button "Sign up" }.to change { User.count }.by(1) - end +feature 'Users' do + around do |ex| + old_url_options = Rails.application.routes.default_url_options + Rails.application.routes.default_url_options = { host: 'example.foo' } + ex.run + Rails.application.routes.default_url_options = old_url_options + end + + scenario 'GET /users/sign_in creates a new user account' do + visit new_user_session_path + fill_in 'user_name', with: 'Name Surname' + fill_in 'user_username', with: 'Great' + fill_in 'user_email', with: 'name@mail.com' + fill_in 'user_password_sign_up', with: 'password1234' + expect { click_button 'Sign up' }.to change { User.count }.by(1) + end + + scenario 'Successful user signin invalidates password reset token' do + user = create(:user) + expect(user.reset_password_token).to be_nil + + visit new_user_password_path + fill_in 'user_email', with: user.email + click_button 'Reset password' + + user.reload + expect(user.reset_password_token).not_to be_nil + + login_with(user) + expect(current_path).to eq root_path + + user.reload + expect(user.reset_password_token).to be_nil end end diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 0d06c6ffb82..944e743675c 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -4,6 +4,11 @@ describe GitlabMarkdownHelper do include ApplicationHelper include IssuesHelper + # TODO: Properly test this + def can?(*) + true + end + let!(:project) { create(:project) } let(:empty_project) { create(:empty_project) } @@ -15,6 +20,9 @@ describe GitlabMarkdownHelper do let(:snippet) { create(:project_snippet, project: project) } let(:member) { project.project_members.where(user_id: user).first } + # Helper expects a current_user method. + let(:current_user) { user } + def url_helper(image_name) File.join(root_url, 'assets', image_name) end diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index aef1108e333..e99c3f5bc11 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe SubmoduleHelper do + include RepoHelpers + describe 'submodule links' do let(:submodule_item) { double(id: 'hash', path: 'rack') } let(:config) { Gitlab.config.gitlab } @@ -111,6 +113,39 @@ describe SubmoduleHelper do expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) end end + + context 'submodules with relative links' do + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } + + before do + self.instance_variable_set(:@project, project) + end + + it 'one level down' do + commit_id = sample_commit[:id] + result = relative_self_links('../test.git', commit_id) + expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"]) + end + + it 'two levels down' do + commit_id = sample_commit[:id] + result = relative_self_links('../../test.git', commit_id) + expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"]) + end + + it 'one level down with namespace and repo' do + commit_id = sample_commit[:id] + result = relative_self_links('../foobar/test.git', commit_id) + expect(result).to eq(["/foobar/test", "/foobar/test/tree/#{commit_id}"]) + end + + it 'two levels down with namespace and repo' do + commit_id = sample_commit[:id] + result = relative_self_links('../foobar/baz/test.git', commit_id) + expect(result).to eq(["/baz/test", "/baz/test/tree/#{commit_id}"]) + end + end end def stub_url(url) diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index c96ee78e5fd..cb7b0fbb890 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -5,126 +5,128 @@ describe Gitlab::ClosingIssueExtractor do let(:issue) { create(:issue, project: project) } let(:iid1) { issue.iid } - describe :closed_by_message_in_project do + subject { described_class.new(project, project.creator) } + + describe "#closed_by_message" do context 'with a single reference' do it do message = "Awesome commit (Closes ##{iid1})" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Awesome commit (closes ##{iid1})" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Closed ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "closed ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Closing ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "closing ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Close ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "close ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Awesome commit (Fixes ##{iid1})" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Awesome commit (fixes ##{iid1})" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Fixed ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "fixed ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Fixing ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "fixing ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Fix ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "fix ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Awesome commit (Resolves ##{iid1})" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Awesome commit (resolves ##{iid1})" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Resolved ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "resolved ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Resolving ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "resolving ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "Resolve ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end it do message = "resolve ##{iid1}" - expect(subject.closed_by_message_in_project(message, project)).to eq([issue]) + expect(subject.closed_by_message(message)).to eq([issue]) end end @@ -137,28 +139,28 @@ describe Gitlab::ClosingIssueExtractor do it 'fetches issues in single line message' do message = "Closes ##{iid1} and fix ##{iid2}" - expect(subject.closed_by_message_in_project(message, project)). + expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end it 'fetches comma-separated issues references in single line message' do message = "Closes ##{iid1}, closes ##{iid2}" - expect(subject.closed_by_message_in_project(message, project)). + expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end it 'fetches comma-separated issues numbers in single line message' do message = "Closes ##{iid1}, ##{iid2} and ##{iid3}" - expect(subject.closed_by_message_in_project(message, project)). + expect(subject.closed_by_message(message)). to eq([issue, other_issue, third_issue]) end it 'fetches issues in multi-line message' do message = "Awesome commit (closes ##{iid1})\nAlso fixes ##{iid2}" - expect(subject.closed_by_message_in_project(message, project)). + expect(subject.closed_by_message(message)). to eq([issue, other_issue]) end @@ -166,7 +168,7 @@ describe Gitlab::ClosingIssueExtractor do message = "Awesome commit (closes ##{iid1})\n"\ "Also fixing issues ##{iid2}, ##{iid3} and #4" - expect(subject.closed_by_message_in_project(message, project)). + expect(subject.closed_by_message(message)). to eq([issue, other_issue, third_issue]) end end diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index b3f4bb5aeda..c9fb62b61ae 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -1,73 +1,76 @@ require 'spec_helper' describe Gitlab::ReferenceExtractor do + let(:project) { create(:project) } + subject { Gitlab::ReferenceExtractor.new(project, project.creator) } + it 'extracts username references' do - subject.analyze('this contains a @user reference', nil) - expect(subject.users).to eq([{ project: nil, id: 'user' }]) + subject.analyze('this contains a @user reference') + expect(subject.references[:user]).to eq([[project, 'user']]) end it 'extracts issue references' do - subject.analyze('this one talks about issue #1234', nil) - expect(subject.issues).to eq([{ project: nil, id: '1234' }]) + subject.analyze('this one talks about issue #1234') + expect(subject.references[:issue]).to eq([[project, '1234']]) end it 'extracts JIRA issue references' do - subject.analyze('this one talks about issue JIRA-1234', nil) - expect(subject.issues).to eq([{ project: nil, id: 'JIRA-1234' }]) + subject.analyze('this one talks about issue JIRA-1234') + expect(subject.references[:issue]).to eq([[project, 'JIRA-1234']]) end it 'extracts merge request references' do - subject.analyze("and here's !43, a merge request", nil) - expect(subject.merge_requests).to eq([{ project: nil, id: '43' }]) + subject.analyze("and here's !43, a merge request") + expect(subject.references[:merge_request]).to eq([[project, '43']]) end it 'extracts snippet ids' do - subject.analyze('snippets like $12 get extracted as well', nil) - expect(subject.snippets).to eq([{ project: nil, id: '12' }]) + subject.analyze('snippets like $12 get extracted as well') + expect(subject.references[:snippet]).to eq([[project, '12']]) end it 'extracts commit shas' do - subject.analyze('commit shas 98cf0ae3 are pulled out as Strings', nil) - expect(subject.commits).to eq([{ project: nil, id: '98cf0ae3' }]) + subject.analyze('commit shas 98cf0ae3 are pulled out as Strings') + expect(subject.references[:commit]).to eq([[project, '98cf0ae3']]) end it 'extracts commit ranges' do - subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4', nil) - expect(subject.commit_ranges).to eq([{ project: nil, id: '98cf0ae3...98cf0ae4' }]) + subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4') + expect(subject.references[:commit_range]).to eq([[project, '98cf0ae3...98cf0ae4']]) end it 'extracts multiple references and preserves their order' do - subject.analyze('@me and @you both care about this', nil) - expect(subject.users).to eq([ - { project: nil, id: 'me' }, - { project: nil, id: 'you' } + subject.analyze('@me and @you both care about this') + expect(subject.references[:user]).to eq([ + [project, 'me'], + [project, 'you'] ]) end it 'leaves the original note unmodified' do text = 'issue #123 is just the worst, @user' - subject.analyze(text, nil) + subject.analyze(text) expect(text).to eq('issue #123 is just the worst, @user') end it 'extracts no references for <pre>..</pre> blocks' do - subject.analyze("<pre>def puts '#1 issue'\nend\n</pre>```", nil) + subject.analyze("<pre>def puts '#1 issue'\nend\n</pre>```") expect(subject.issues).to be_blank end it 'extracts no references for <code>..</code> blocks' do - subject.analyze("<code>def puts '!1 request'\nend\n</code>```", nil) + subject.analyze("<code>def puts '!1 request'\nend\n</code>```") expect(subject.merge_requests).to be_blank end it 'extracts no references for code blocks with language' do - subject.analyze("this code:\n```ruby\ndef puts '#1 issue'\nend\n```", nil) + subject.analyze("this code:\n```ruby\ndef puts '#1 issue'\nend\n```") expect(subject.issues).to be_blank end it 'extracts issue references for invalid code blocks' do - subject.analyze('test: ```this one talks about issue #1234```', nil) - expect(subject.issues).to eq([{ project: nil, id: '1234' }]) + subject.analyze('test: ```this one talks about issue #1234```') + expect(subject.references[:issue]).to eq([[project, '1234']]) end it 'handles all possible kinds of references' do @@ -75,83 +78,79 @@ describe Gitlab::ReferenceExtractor do expect(subject).to respond_to(*accessors) end - context 'with a project' do - let(:project) { create(:project) } - - it 'accesses valid user objects on the project team' do - @u_foo = create(:user, username: 'foo') - @u_bar = create(:user, username: 'bar') - create(:user, username: 'offteam') + it 'accesses valid user objects' do + @u_foo = create(:user, username: 'foo') + @u_bar = create(:user, username: 'bar') + @u_offteam = create(:user, username: 'offteam') - project.team << [@u_foo, :reporter] - project.team << [@u_bar, :guest] + project.team << [@u_foo, :reporter] + project.team << [@u_bar, :guest] - subject.analyze('@foo, @baduser, @bar, and @offteam', project) - expect(subject.users_for(project)).to eq([@u_foo, @u_bar]) - end + subject.analyze('@foo, @baduser, @bar, and @offteam') + expect(subject.users).to eq([@u_foo, @u_bar, @u_offteam]) + end - it 'accesses valid issue objects' do - @i0 = create(:issue, project: project) - @i1 = create(:issue, project: project) + it 'accesses valid issue objects' do + @i0 = create(:issue, project: project) + @i1 = create(:issue, project: project) - subject.analyze("##{@i0.iid}, ##{@i1.iid}, and #999.", project) - expect(subject.issues_for(project)).to eq([@i0, @i1]) - end + subject.analyze("##{@i0.iid}, ##{@i1.iid}, and #999.") + expect(subject.issues).to eq([@i0, @i1]) + 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') + 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') - subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.", project) - expect(subject.merge_requests_for(project)).to eq([@m1, @m0]) - end + subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.") + expect(subject.merge_requests).to eq([@m1, @m0]) + end - it 'accesses valid snippets' do - @s0 = create(:project_snippet, project: project) - @s1 = create(:project_snippet, project: project) - @s2 = create(:project_snippet) + it 'accesses valid snippets' do + @s0 = create(:project_snippet, project: project) + @s1 = create(:project_snippet, project: project) + @s2 = create(:project_snippet) - subject.analyze("$#{@s0.id}, $999, $#{@s2.id}, $#{@s1.id}", project) - expect(subject.snippets_for(project)).to eq([@s0, @s1]) - end + subject.analyze("$#{@s0.id}, $999, $#{@s2.id}, $#{@s1.id}") + expect(subject.snippets).to eq([@s0, @s1]) + end - it 'accesses valid commits' do - commit = project.repository.commit('master') + it 'accesses valid commits' do + commit = project.repository.commit('master') - subject.analyze("this references commits #{commit.sha[0..6]} and 012345", - project) - extracted = subject.commits_for(project) - expect(extracted.size).to eq(1) - expect(extracted[0].sha).to eq(commit.sha) - expect(extracted[0].message).to eq(commit.message) - end + subject.analyze("this references commits #{commit.sha[0..6]} and 012345") + extracted = subject.commits + expect(extracted.size).to eq(1) + expect(extracted[0].sha).to eq(commit.sha) + expect(extracted[0].message).to eq(commit.message) + end - it 'accesses valid commit ranges' do - commit = project.repository.commit('master') - earlier_commit = project.repository.commit('master~2') + it 'accesses valid commit ranges' do + commit = project.repository.commit('master') + earlier_commit = project.repository.commit('master~2') - subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}", - project) - extracted = subject.commit_ranges_for(project) - expect(extracted.size).to eq(1) - expect(extracted[0][0].sha).to eq(earlier_commit.sha) - expect(extracted[0][0].message).to eq(earlier_commit.message) - expect(extracted[0][1].sha).to eq(commit.sha) - expect(extracted[0][1].message).to eq(commit.message) - end + subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}") + extracted = subject.commit_ranges + expect(extracted.size).to eq(1) + expect(extracted[0][0].sha).to eq(earlier_commit.sha) + expect(extracted[0][0].message).to eq(earlier_commit.message) + expect(extracted[0][1].sha).to eq(commit.sha) + expect(extracted[0][1].message).to eq(commit.message) end context 'with a project with an underscore' do - let(:project) { create(:project, path: 'test_project') } - let(:issue) { create(:issue, project: project) } + let(:other_project) { create(:project, path: 'test_project') } + let(:issue) { create(:issue, project: other_project) } + + before do + other_project.team << [project.creator, :developer] + end it 'handles project issue references' do - subject.analyze("this refers issue #{project.path_with_namespace}##{issue.iid}", - project) - extracted = subject.issues_for(project) + subject.analyze("this refers issue #{other_project.path_with_namespace}##{issue.iid}") + extracted = subject.issues expect(extracted.size).to eq(1) expect(extracted).to eq([issue]) end - end end diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index f351aab9238..7032b777144 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -28,17 +28,32 @@ describe DeployKeysProject do let(:deploy_key) { subject.deploy_key } context "when the deploy key is only used by this project" do - it "destroys the deploy key" do - subject.destroy + context "when the deploy key is public" do + before do + deploy_key.update_attribute(:public, true) + end - expect { - deploy_key.reload - }.to raise_error(ActiveRecord::RecordNotFound) + it "doesn't destroy the deploy key" do + subject.destroy + + expect { + deploy_key.reload + }.not_to raise_error(ActiveRecord::RecordNotFound) + end + end + + context "when the deploy key is private" do + it "destroys the deploy key" do + subject.destroy + + expect { + deploy_key.reload + }.to raise_error(ActiveRecord::RecordNotFound) + end end end context "when the deploy key is used by more than one project" do - let!(:other_project) { create(:project) } before do diff --git a/spec/models/project_services/buildbox_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 9f29fbe12b0..e987241f3ca 100644 --- a/spec/models/project_services/buildbox_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -19,7 +19,7 @@ require 'spec_helper' -describe BuildboxService do +describe BuildkiteService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -32,7 +32,7 @@ describe BuildboxService do default_branch: 'default-brancho' ) - @service = BuildboxService.new + @service = BuildkiteService.new @service.stub( project: @project, service_hook: true, diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index 610f33c5823..6a557d839ca 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -46,4 +46,25 @@ describe GitlabCiService do it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c")} end end + + describe "Fork registration" do + before do + @old_project = create(:empty_project) + @project = create(:empty_project) + @user = create(:user) + + @service = GitlabCiService.new + @service.stub( + service_hook: true, + project_url: 'http://ci.gitlab.org/projects/2', + token: 'verySecret', + project: @old_project + ) + end + + it "performs http reuquest to ci" do + stub_request(:post, "http://ci.gitlab.org/api/v1/forks") + @service.fork_registration(@project, @user.private_token) + end + end end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index d9bd91f5990..042352311da 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -168,12 +168,11 @@ end # project_deploy_keys GET /:project_id/deploy_keys(.:format) deploy_keys#index # POST /:project_id/deploy_keys(.:format) deploy_keys#create # new_project_deploy_key GET /:project_id/deploy_keys/new(.:format) deploy_keys#new -# edit_project_deploy_key GET /:project_id/deploy_keys/:id/edit(.:format) deploy_keys#edit # project_deploy_key GET /:project_id/deploy_keys/:id(.:format) deploy_keys#show -# PUT /:project_id/deploy_keys/:id(.:format) deploy_keys#update # DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy describe Projects::DeployKeysController, 'routing' do it_behaves_like 'RESTful project resources' do + let(:actions) { [:index, :show, :new, :create] } let(:controller) { 'deploy_keys' } end end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index e55a2e3f8a0..c9025bdf133 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -40,6 +40,17 @@ describe Projects::ForkService do expect(@to_project.errors[:base]).not_to include("Fork transaction failed.") end end + + context 'GitLab CI is enabled' do + it "calls fork registrator for CI" do + @from_project.build_missing_services + @from_project.gitlab_ci_service.update_attributes(active: true) + + expect(ForkRegistrationWorker).to receive(:perform_async) + + fork_project(@from_project, @to_user) + end + end end describe :fork_to_namespace do @@ -89,7 +100,8 @@ describe Projects::ForkService do def fork_project(from_project, user, fork_success = true, params = {}) context = Projects::ForkService.new(from_project, user, params) - shell = double('gitlab_shell').stub(fork_repository: fork_success) + shell = double('gitlab_shell') + shell.stub(fork_repository: fork_success) context.stub(gitlab_shell: shell) context.execute end diff --git a/spec/workers/fork_registration_worker_spec.rb b/spec/workers/fork_registration_worker_spec.rb new file mode 100644 index 00000000000..cc6f574b29c --- /dev/null +++ b/spec/workers/fork_registration_worker_spec.rb @@ -0,0 +1,10 @@ + +require 'spec_helper' + +describe ForkRegistrationWorker do + context "as a resque worker" do + it "reponds to #perform" do + expect(ForkRegistrationWorker.new).to respond_to(:perform) + end + end +end |
