diff --git a/app/controllers/deploy_keys_controller.rb b/app/controllers/deploy_keys_controller.rb index a89ebbcb8d5..ca5c605aa7a 100644 --- a/app/controllers/deploy_keys_controller.rb +++ b/app/controllers/deploy_keys_controller.rb @@ -5,7 +5,8 @@ class DeployKeysController < ProjectResourceController before_filter :authorize_admin_project! def index - @keys = @project.deploy_keys.all + @enabled_keys = @project.deploy_keys.all + @available_keys = available_keys - @enabled_keys end def show @@ -19,8 +20,9 @@ class DeployKeysController < ProjectResourceController end def create - @key = @project.deploy_keys.new(params[:key]) - if @key.save + @key = DeployKey.new(params[:deploy_key]) + + if @key.valid? && @project.deploy_keys << @key redirect_to project_deploy_keys_path(@project) else render "new" @@ -36,4 +38,22 @@ class DeployKeysController < ProjectResourceController format.js { render nothing: true } end end + + def enable + project.deploy_keys << available_keys.find(params[:id]) + + redirect_to project_deploy_keys_path(@project) + end + + def disable + @project.deploy_keys_projects.where(deploy_key_id: params[:id]).last.destroy + + redirect_to project_deploy_keys_path(@project) + end + + protected + + def available_keys + @available_keys ||= DeployKey.in_projects(current_user.owned_projects) + end end diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb new file mode 100644 index 00000000000..548ef4f9a27 --- /dev/null +++ b/app/models/deploy_key.rb @@ -0,0 +1,6 @@ +class DeployKey < Key + has_many :deploy_keys_projects, dependent: :destroy + has_many :projects, through: :deploy_keys_projects + + scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) } +end diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb new file mode 100644 index 00000000000..48350a3e4d9 --- /dev/null +++ b/app/models/deploy_keys_project.rb @@ -0,0 +1,11 @@ +class DeployKeysProject < ActiveRecord::Base + attr_accessible :key_id, :project_id + + belongs_to :project + belongs_to :deploy_key + + validates :deploy_key_id, presence: true + validates :deploy_key_id, uniqueness: { scope: [:project_id], message: "already exists in project" } + + validates :project_id, presence: true +end diff --git a/app/models/key.rb b/app/models/key.rb index ce62b802c0d..a72357a6d01 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -16,7 +16,6 @@ require 'digest/md5' class Key < ActiveRecord::Base belongs_to :user - belongs_to :project attr_accessible :key, :title @@ -29,7 +28,7 @@ class Key < ActiveRecord::Base delegate :name, :email, to: :user, prefix: true def strip_white_space - self.key = self.key.strip unless self.key.blank? + self.key = key.strip unless key.blank? end def fingerprintable_key @@ -47,20 +46,12 @@ class Key < ActiveRecord::Base errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0 end - def is_deploy_key - project.present? - end - # projects that has this key def projects - if is_deploy_key - [project] - else - user.authorized_projects - end + user.authorized_projects end def shell_id - "key-#{self.id}" + "key-#{id}" end end diff --git a/app/models/project.rb b/app/models/project.rb index 291316f3088..203ccb8e290 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -55,7 +55,6 @@ class Project < ActiveRecord::Base has_many :users_projects, dependent: :destroy has_many :notes, dependent: :destroy has_many :snippets, dependent: :destroy - has_many :deploy_keys, dependent: :destroy, class_name: "Key", foreign_key: "project_id" has_many :hooks, dependent: :destroy, class_name: "ProjectHook" has_many :protected_branches, dependent: :destroy has_many :user_team_project_relationships, dependent: :destroy @@ -65,6 +64,9 @@ class Project < ActiveRecord::Base has_many :user_team_user_relationships, through: :user_teams has_many :user_teams_members, through: :user_team_user_relationships + has_many :deploy_keys_projects, dependent: :destroy + has_many :deploy_keys, through: :deploy_keys_projects + delegate :name, to: :owner, allow_nil: true, prefix: true # Validations diff --git a/app/models/user.rb b/app/models/user.rb index fb2e62279a5..1d90ec3b37a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -89,7 +89,7 @@ class User < ActiveRecord::Base has_many :personal_projects, through: :namespace, source: :projects has_many :projects, through: :users_projects - has_many :own_projects, foreign_key: :creator_id + has_many :own_projects, foreign_key: :creator_id, class_name: 'Project' has_many :owned_projects, through: :namespaces, source: :projects # diff --git a/app/views/deploy_keys/_deploy_key.html.haml b/app/views/deploy_keys/_deploy_key.html.haml new file mode 100644 index 00000000000..274015cb529 --- /dev/null +++ b/app/views/deploy_keys/_deploy_key.html.haml @@ -0,0 +1,25 @@ +%li + .pull-right + - if @available_keys.include?(deploy_key) + = link_to enable_project_deploy_key_path(@project, deploy_key), class: 'btn btn-small', method: :put do + %i.icon-plus + Enable + - else + - if deploy_key.projects.count > 1 + = link_to disable_project_deploy_key_path(@project, deploy_key), class: 'btn btn-small', method: :put do + %i.icon-off + Disable + - else + = link_to 'Remove', project_deploy_key_path(@project, deploy_key), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key btn-small pull-right" + + + = link_to project_deploy_key_path(@project, deploy_key) do + %i.icon-key + %strong= deploy_key.title + + %p.light.prepend-top-10 + - deploy_key.projects.map(&:name_with_namespace).each do |project_name| + %span.label= project_name + %small.pull-right + Created #{time_ago_in_words(deploy_key.created_at)} ago + diff --git a/app/views/deploy_keys/_form.html.haml b/app/views/deploy_keys/_form.html.haml index 5fb83021dc0..71bf309dd8b 100644 --- a/app/views/deploy_keys/_form.html.haml +++ b/app/views/deploy_keys/_form.html.haml @@ -18,6 +18,6 @@ = link_to "here", help_ssh_path .actions - = f.submit 'Save', class: "btn-save btn" + = f.submit 'Create', class: "btn-create btn" = link_to "Cancel", project_deploy_keys_path(@project), class: "btn btn-cancel" diff --git a/app/views/deploy_keys/_show.html.haml b/app/views/deploy_keys/_show.html.haml deleted file mode 100644 index 635054350ec..00000000000 --- a/app/views/deploy_keys/_show.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -%tr - %td - %a{href: project_deploy_key_path(key.project, key)} - %strong= key.title - %td - %span.update-author - Added - = time_ago_in_words(key.created_at) - ago - %td - = link_to 'Remove', project_deploy_key_path(key.project, key), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key btn-small pull-right" - diff --git a/app/views/deploy_keys/index.html.haml b/app/views/deploy_keys/index.html.haml index 8fa9d5f3bca..3e8a8730a7a 100644 --- a/app/views/deploy_keys/index.html.haml +++ b/app/views/deploy_keys/index.html.haml @@ -1,17 +1,26 @@ = render "projects/settings_nav" %p.slead - Deploy keys allow read-only access to repository. They can be used for CI, staging or production servers. A deploy key can be added to only one project. If you need to add the same key to multiple projects you can create a deploy user and add that user to multiple projects. + Deploy keys allow read-only access to repository. They can be used for CI, staging or production servers - - if can? current_user, :admin_project, @project - = link_to new_project_deploy_key_path(@project), class: "btn btn-small", title: "New Deploy Key" do - Add Deploy Key -- if @keys.any? - %table - %thead - %tr - %th Keys - %th - %th - - @keys.each do |key| - = render(partial: 'show', locals: {key: key}) +%p + You can create a deploy key or add existing one + = link_to new_project_deploy_key_path(@project), class: "btn btn-primary pull-right", title: "New Deploy Key" do + %i.icon-plus + New Deploy Key + +%hr.clearfix + +.row + .span6.enabled-keys + %h5.cgreen + Enabled deploy keys + %small for this project + %ul.bordered-list + = render @enabled_keys + .span6.available-keys + %h5 + Available deploy keys + %small from projects you are able to manage + %ul.bordered-list + = render @available_keys diff --git a/app/views/deploy_keys/show.html.haml b/app/views/deploy_keys/show.html.haml index 0a9f376d046..5b59d322343 100644 --- a/app/views/deploy_keys/show.html.haml +++ b/app/views/deploy_keys/show.html.haml @@ -12,4 +12,4 @@ %hr %pre= @key.key .pull-right - = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "btn-remove btn delete-key" + = link_to 'Remove', project_deploy_key_path(@project, @key), confirm: 'Are you sure?', method: :delete, class: "btn-remove btn delete-key" diff --git a/app/views/refs/logs_tree.js.haml b/app/views/refs/logs_tree.js.haml index 23a6dae7810..0b517327139 100644 --- a/app/views/refs/logs_tree.js.haml +++ b/app/views/refs/logs_tree.js.haml @@ -1,4 +1,4 @@ -- @logs.each do |content_data| +- @logs.each do |content_data| - file_name = content_data[:file_name] - commit = content_data[:commit] diff --git a/config/routes.rb b/config/routes.rb index 8bd6307357a..33b4ac1a3bd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -215,7 +215,13 @@ Gitlab::Application.routes.draw do end end - resources :deploy_keys + resources :deploy_keys do + member do + put :enable + put :disable + end + end + resources :protected_branches, only: [:index, :create, :destroy] resources :refs, only: [] do diff --git a/db/migrate/20130506085413_add_type_to_key.rb b/db/migrate/20130506085413_add_type_to_key.rb new file mode 100644 index 00000000000..315e7ca77b3 --- /dev/null +++ b/db/migrate/20130506085413_add_type_to_key.rb @@ -0,0 +1,5 @@ +class AddTypeToKey < ActiveRecord::Migration + def change + add_column :keys, :type, :string + end +end diff --git a/db/migrate/20130506090604_create_deploy_keys_projects.rb b/db/migrate/20130506090604_create_deploy_keys_projects.rb new file mode 100644 index 00000000000..0dc8cdeb07d --- /dev/null +++ b/db/migrate/20130506090604_create_deploy_keys_projects.rb @@ -0,0 +1,10 @@ +class CreateDeployKeysProjects < ActiveRecord::Migration + def change + create_table :deploy_keys_projects do |t| + t.integer :deploy_key_id, null: false + t.integer :project_id, null: false + + t.timestamps + end + end +end diff --git a/db/migrate/20130506095501_remove_project_id_from_key.rb b/db/migrate/20130506095501_remove_project_id_from_key.rb new file mode 100644 index 00000000000..4214fd45d14 --- /dev/null +++ b/db/migrate/20130506095501_remove_project_id_from_key.rb @@ -0,0 +1,22 @@ +class RemoveProjectIdFromKey < ActiveRecord::Migration + def up + puts 'Migrate deploy keys: ' + Key.where('project_id IS NOT NULL').update_all(type: 'DeployKey') + + DeployKey.all.each do |key| + project = Project.find_by_id(key.project_id) + if project + project.deploy_keys << key + print '.' + end + end + + puts 'Done' + + remove_column :keys, :project_id + end + + def down + add_column :keys, :project_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 1af91a3b8ee..d2fa70cc374 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,14 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130410175022) do +ActiveRecord::Schema.define(:version => 20130506095501) do + + create_table "deploy_keys_projects", :force => true do |t| + t.integer "deploy_key_id", :null => false + t.integer "project_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end create_table "events", :force => true do |t| t.string "target_type" @@ -46,8 +53,8 @@ ActiveRecord::Schema.define(:version => 20130410175022) do t.integer "assignee_id" t.integer "author_id" t.integer "project_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.integer "position", :default => 0 t.string "branch_name" t.text "description" @@ -64,16 +71,15 @@ ActiveRecord::Schema.define(:version => 20130410175022) do create_table "keys", :force => true do |t| t.integer "user_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.text "key" t.string "title" t.string "identifier" - t.integer "project_id" + t.string "type" end add_index "keys", ["identifier"], :name => "index_keys_on_identifier" - add_index "keys", ["project_id"], :name => "index_keys_on_project_id" add_index "keys", ["user_id"], :name => "index_keys_on_user_id" create_table "merge_requests", :force => true do |t| @@ -83,8 +89,8 @@ ActiveRecord::Schema.define(:version => 20130410175022) do t.integer "author_id" t.integer "assignee_id" t.string "title" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.text "st_commits", :limit => 2147483647 t.text "st_diffs", :limit => 2147483647 t.integer "milestone_id" @@ -133,8 +139,8 @@ ActiveRecord::Schema.define(:version => 20130410175022) do t.text "note" t.string "noteable_type" t.integer "author_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.integer "project_id" t.string "attachment" t.string "line_code" @@ -152,8 +158,8 @@ ActiveRecord::Schema.define(:version => 20130410175022) do t.string "name" t.string "path" t.text "description" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.integer "creator_id" t.string "default_branch" t.boolean "issues_enabled", :default => true, :null => false @@ -197,8 +203,8 @@ ActiveRecord::Schema.define(:version => 20130410175022) do t.text "content" t.integer "author_id", :null => false t.integer "project_id", :null => false - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "file_name" t.datetime "expires_at" end @@ -217,6 +223,9 @@ ActiveRecord::Schema.define(:version => 20130410175022) do t.datetime "created_at" end + add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id" + add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context" + create_table "tags", :force => true do |t| t.string "name" end @@ -248,41 +257,42 @@ ActiveRecord::Schema.define(:version => 20130410175022) do end create_table "users", :force => true do |t| - t.string "email", :default => "", :null => false - t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "email", :default => "", :null => false + t.string "encrypted_password", :default => "", :null => false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", :default => 0 + t.integer "sign_in_count", :default => 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "name" - t.boolean "admin", :default => false, :null => false - t.integer "projects_limit", :default => 10 - t.string "skype", :default => "", :null => false - t.string "linkedin", :default => "", :null => false - t.string "twitter", :default => "", :null => false + t.boolean "admin", :default => false, :null => false + t.integer "projects_limit", :default => 10 + t.string "skype", :default => "", :null => false + t.string "linkedin", :default => "", :null => false + t.string "twitter", :default => "", :null => false t.string "authentication_token" - t.integer "theme_id", :default => 1, :null => false + t.integer "theme_id", :default => 1, :null => false t.string "bio" - t.integer "failed_attempts", :default => 0 + t.integer "failed_attempts", :default => 0 t.datetime "locked_at" t.string "extern_uid" t.string "provider" t.string "username" - t.boolean "can_create_group", :default => true, :null => false - t.boolean "can_create_team", :default => true, :null => false + t.boolean "can_create_group", :default => true, :null => false + t.boolean "can_create_team", :default => true, :null => false t.string "state" - t.integer "color_scheme_id", :default => 1, :null => false - t.integer "notification_level", :default => 1, :null => false + t.integer "color_scheme_id", :default => 1, :null => false + t.integer "notification_level", :default => 1, :null => false end add_index "users", ["admin"], :name => "index_users_on_admin" add_index "users", ["email"], :name => "index_users_on_email", :unique => true + add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true add_index "users", ["name"], :name => "index_users_on_name" add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true add_index "users", ["username"], :name => "index_users_on_username" @@ -290,8 +300,8 @@ ActiveRecord::Schema.define(:version => 20130410175022) do create_table "users_projects", :force => true do |t| t.integer "user_id", :null => false t.integer "project_id", :null => false - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.integer "project_access", :default => 0, :null => false t.integer "notification_level", :default => 3, :null => false end @@ -303,8 +313,8 @@ ActiveRecord::Schema.define(:version => 20130410175022) do create_table "web_hooks", :force => true do |t| t.string "url" t.integer "project_id" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "type", :default => "ProjectHook" t.integer "service_id" end diff --git a/features/project/deploy_keys.feature b/features/project/deploy_keys.feature new file mode 100644 index 00000000000..13e3b9bbd2e --- /dev/null +++ b/features/project/deploy_keys.feature @@ -0,0 +1,23 @@ +Feature: Project Deploy Keys + Background: + Given I sign in as a user + And I own project "Shop" + + 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 + + Scenario: I add new deploy key + Given I visit project deploy keys page + When I click 'New Deploy Key' + And I submit new deploy key + Then I should be on deploy keys page + And I should see newly created deploy key + + Scenario: I attach 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 diff --git a/features/steps/project/deploy_keys.rb b/features/steps/project/deploy_keys.rb new file mode 100644 index 00000000000..fd9dce7fe30 --- /dev/null +++ b/features/steps/project/deploy_keys.rb @@ -0,0 +1,52 @@ +class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps + include SharedAuthentication + include SharedProject + include SharedPaths + + step 'project has deploy key' do + create(:deploy_keys_project, project: @project) + end + + step 'I should see project deploy keys' do + within '.enabled-keys' do + page.should have_content deploy_key.title + end + 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 deploy keys page' do + current_path.should == project_deploy_keys_path(@project) + end + + step 'I should see newly created deploy key' do + within '.enabled-keys' do + page.should have_content(deploy_key.title) + end + end + + step 'other project has deploy key' do + @second_project = create :project, namespace: current_user.namespace + create(:deploy_keys_project, project: @second_project) + end + + step 'I click attach deploy key' do + within '.available-keys' do + click_link 'Enable' + end + end + + protected + + def deploy_key + @project.deploy_keys.last + end +end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 38730cc2cd6..b0d3b0f2a03 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -1,7 +1,7 @@ module SharedPaths include Spinach::DSL - When 'I visit new project page' do + step 'I visit new project page' do visit new_project_path end @@ -9,23 +9,23 @@ module SharedPaths # Group # ---------------------------------------- - When 'I visit group page' do + step 'I visit group page' do visit group_path(current_group) end - When 'I visit group issues page' do + step 'I visit group issues page' do visit issues_group_path(current_group) end - When 'I visit group merge requests page' do + step 'I visit group merge requests page' do visit merge_requests_group_path(current_group) end - When 'I visit group people page' do + step 'I visit group people page' do visit people_group_path(current_group) end - When 'I visit group settings page' do + step 'I visit group settings page' do visit edit_group_path(current_group) end @@ -33,27 +33,27 @@ module SharedPaths # Dashboard # ---------------------------------------- - Given 'I visit dashboard page' do + step 'I visit dashboard page' do visit dashboard_path end - Given 'I visit dashboard projects page' do + step 'I visit dashboard projects page' do visit projects_dashboard_path end - Given 'I visit dashboard issues page' do + step 'I visit dashboard issues page' do visit issues_dashboard_path end - Given 'I visit dashboard merge requests page' do + step 'I visit dashboard merge requests page' do visit merge_requests_dashboard_path end - Given 'I visit dashboard search page' do + step 'I visit dashboard search page' do visit search_path end - Given 'I visit dashboard help page' do + step 'I visit dashboard help page' do visit help_path end @@ -61,23 +61,23 @@ module SharedPaths # Profile # ---------------------------------------- - Given 'I visit profile page' do + step 'I visit profile page' do visit profile_path end - Given 'I visit profile account page' do + step 'I visit profile account page' do visit account_profile_path end - Given 'I visit profile SSH keys page' do + step 'I visit profile SSH keys page' do visit keys_path end - Given 'I visit profile design page' do + step 'I visit profile design page' do visit design_profile_path end - Given 'I visit profile history page' do + step 'I visit profile history page' do visit history_profile_path end @@ -85,35 +85,35 @@ module SharedPaths # Admin # ---------------------------------------- - Given 'I visit admin page' do + step 'I visit admin page' do visit admin_root_path end - Given 'I visit admin projects page' do + step 'I visit admin projects page' do visit admin_projects_path end - Given 'I visit admin users page' do + step 'I visit admin users page' do visit admin_users_path end - Given 'I visit admin logs page' do + step 'I visit admin logs page' do visit admin_logs_path end - Given 'I visit admin hooks page' do + step 'I visit admin hooks page' do visit admin_hooks_path end - Given 'I visit admin Resque page' do + step 'I visit admin Resque page' do visit admin_resque_path end - And 'I visit admin groups page' do + step 'I visit admin groups page' do visit admin_groups_path end - When 'I visit admin teams page' do + step 'I visit admin teams page' do visit admin_teams_path end @@ -121,145 +121,149 @@ module SharedPaths # Generic Project # ---------------------------------------- - Given "I visit my project's home page" do + step "I visit my project's home page" do visit project_path(@project) end - Given "I visit my project's settings page" do + step "I visit my project's settings page" do visit edit_project_path(@project) end - Given "I visit my project's files page" do + step "I visit my project's files page" do visit project_tree_path(@project, root_ref) end - Given "I visit my project's commits page" do + step "I visit my project's commits page" do visit project_commits_path(@project, root_ref, {limit: 5}) end - Given "I visit my project's commits page for a specific path" do + step "I visit my project's commits page for a specific path" do visit project_commits_path(@project, root_ref + "/app/models/project.rb", {limit: 5}) end - Given 'I visit my project\'s commits stats page' do + step 'I visit my project\'s commits stats page' do visit stats_project_repository_path(@project) end - Given "I visit my project's network page" do + step "I visit my project's network page" do # Stub Graph max_size to speed up test (10 commits vs. 650) Network::Graph.stub(max_count: 10) visit project_graph_path(@project, root_ref) end - Given "I visit my project's issues page" do + step "I visit my project's issues page" do visit project_issues_path(@project) end - Given "I visit my project's merge requests page" do + step "I visit my project's merge requests page" do visit project_merge_requests_path(@project) end - Given "I visit my project's wall page" do + step "I visit my project's wall page" do visit project_wall_path(@project) end - Given "I visit my project's wiki page" do + step "I visit my project's wiki page" do visit project_wiki_path(@project, :home) end - When 'I visit project hooks page' do + step 'I visit project hooks page' do visit project_hooks_path(@project) end + step 'I visit project deploy keys page' do + visit project_deploy_keys_path(@project) + end + # ---------------------------------------- # "Shop" Project # ---------------------------------------- - And 'I visit project "Shop" page' do + step 'I visit project "Shop" page' do visit project_path(project) end - When 'I visit edit project "Shop" page' do + step 'I visit edit project "Shop" page' do visit edit_project_path(project) end - Given 'I visit project branches page' do + step 'I visit project branches page' do visit branches_project_repository_path(@project) end - Given 'I visit compare refs page' do + step 'I visit compare refs page' do visit project_compare_index_path(@project) end - Given 'I visit project commits page' do + step 'I visit project commits page' do visit project_commits_path(@project, root_ref, {limit: 5}) end - Given 'I visit project commits page for stable branch' do + step 'I visit project commits page for stable branch' do visit project_commits_path(@project, 'stable', {limit: 5}) end - Given 'I visit project source page' do + step 'I visit project source page' do visit project_tree_path(@project, root_ref) end - Given 'I visit blob file from repo' do + step 'I visit blob file from repo' do visit project_blob_path(@project, File.join(ValidCommit::ID, ValidCommit::BLOB_FILE_PATH)) end - Given 'I visit project source page for "8470d70"' do + step 'I visit project source page for "8470d70"' do visit project_tree_path(@project, "8470d70") end - Given 'I visit project tags page' do + step 'I visit project tags page' do visit tags_project_repository_path(@project) end - Given 'I visit project commit page' do + step 'I visit project commit page' do visit project_commit_path(@project, ValidCommit::ID) end - And 'I visit project "Shop" issues page' do + step 'I visit project "Shop" issues page' do visit project_issues_path(project) end - Given 'I visit issue page "Release 0.4"' do + step 'I visit issue page "Release 0.4"' do issue = Issue.find_by_title("Release 0.4") visit project_issue_path(issue.project, issue) end - Given 'I visit project "Shop" labels page' do + step 'I visit project "Shop" labels page' do visit project_labels_path(project) end - Given 'I visit merge request page "Bug NS-04"' do + step 'I visit merge request page "Bug NS-04"' do mr = MergeRequest.find_by_title("Bug NS-04") visit project_merge_request_path(mr.project, mr) end - Given 'I visit merge request page "Bug NS-05"' do + step 'I visit merge request page "Bug NS-05"' do mr = MergeRequest.find_by_title("Bug NS-05") visit project_merge_request_path(mr.project, mr) end - And 'I visit project "Shop" merge requests page' do + step 'I visit project "Shop" merge requests page' do visit project_merge_requests_path(project) end - Given 'I visit project "Shop" milestones page' do + step 'I visit project "Shop" milestones page' do visit project_milestones_path(project) end - Then 'I visit project "Shop" team page' do + step 'I visit project "Shop" team page' do visit project_team_index_path(project) end - Then 'I visit project "Shop" wall page' do + step 'I visit project "Shop" wall page' do visit project_wall_path(project) end - Given 'I visit project wiki page' do + step 'I visit project wiki page' do visit project_wiki_path(@project, :home) end @@ -267,7 +271,7 @@ module SharedPaths # Public Projects # ---------------------------------------- - Given 'I visit the public projects area' do + step 'I visit the public projects area' do visit public_root_path end diff --git a/lib/api/internal.rb b/lib/api/internal.rb index affe1be54dd..3e1173bd0a3 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -25,8 +25,8 @@ module Gitlab return false unless project - if key.is_deploy_key - project == key.project && git_cmd == 'git-upload-pack' + if key.is_a? DeployKey + key.projects.include?(project) && git_cmd == 'git-upload-pack' else user = key.user diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 75157e55730..53fd91dcca8 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -531,8 +531,8 @@ module Gitlab # POST /projects/:id/keys post ":id/keys" do attrs = attributes_for_keys [:title, :key] - key = user_project.deploy_keys.new attrs - if key.save + key = DeployKey.new attrs + if key.valid? && user_project.deploy_keys << key present key, with: Entities::SSHKey else not_found! @@ -545,9 +545,8 @@ module Gitlab # DELETE /projects/:id/keys/:id delete ":id/keys/:key_id" do key = user_project.deploy_keys.find params[:key_id] - key.delete + key.destroy end - end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 4198387d403..cda543495a0 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -174,7 +174,7 @@ module Gitlab delete "keys/:id" do begin key = current_user.keys.find params[:id] - key.delete + key.destroy rescue end end diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 773e496ee41..7e725a5e621 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -24,6 +24,7 @@ namespace :gitlab do check_init_script_up_to_date check_satellites_exist check_redis_version + check_git_version finished_checking "GitLab" end @@ -663,4 +664,18 @@ namespace :gitlab do puts "FAIL. Please update gitlab-shell to v#{required_version}".red end end + + def check_git_version + print "Git version >= 1.7.10 ? ... " + + if run_and_match("git --version", /git version 1.7.10.\d/) + puts "yes".green + else + puts "no".red + try_fixing_it( + "Update your git to a version >= 1.7.10" + ) + fix_and_rerun + end + end end diff --git a/spec/factories.rb b/spec/factories.rb index 8f323161990..f9e25382b61 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -158,8 +158,7 @@ FactoryGirl.define do "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" end - factory :deploy_key do - project + factory :deploy_key, class: 'DeployKey' do end factory :personal_key do @@ -222,4 +221,9 @@ FactoryGirl.define do url service end + + factory :deploy_keys_project do + deploy_key + project + end end diff --git a/spec/features/projects_deploy_keys_spec.rb b/spec/features/projects_deploy_keys_spec.rb deleted file mode 100644 index 25b1da9ebd8..00000000000 --- a/spec/features/projects_deploy_keys_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'spec_helper' - -describe "Projects", "DeployKeys" do - let(:project) { create(:project) } - - before do - login_as :user - project.team << [@user, :master] - end - - describe "GET /keys" do - before do - @key = create(:key, project: project) - visit project_deploy_keys_path(project) - end - - subject { page } - - it { should have_content(@key.title) } - - describe "Destroy" do - before { visit project_deploy_key_path(project, @key) } - - it "should remove entry" do - expect { - click_link "Remove" - }.to change { project.deploy_keys.count }.by(-1) - end - end - end - - describe "New key" do - before do - visit project_deploy_keys_path(project) - click_link "New Deploy Key" - end - - it "should open new key popup" do - page.should have_content("New Deploy key") - end - - describe "fill in" do - before do - fill_in "key_title", with: "laptop" - fill_in "key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop" - end - - it { expect { click_button "Save" }.to change {Key.count}.by(1) } - - it "should add new key to table" do - click_button "Save" - - page.should have_content "laptop" - end - end - end - - describe "Show page" do - before do - @key = create(:key, project: project) - visit project_deploy_key_path(project, @key) - end - - it { page.should have_content @key.title } - it { page.should have_content @key.key[0..10] } - end -end diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb new file mode 100644 index 00000000000..3658a6ff1d0 --- /dev/null +++ b/spec/models/deploy_key_spec.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: keys +# +# id :integer not null, primary key +# user_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# key :text +# title :string(255) +# identifier :string(255) +# project_id :integer +# + +require 'spec_helper' + +describe DeployKey do + let(:project) { create(:project) } + let(:deploy_key) { create(:deploy_key, projects: [project]) } + + describe "Associations" do + it { should have_many(:deploy_keys_projects) } + it { should have_many(:projects) } + end +end diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb new file mode 100644 index 00000000000..bb62c48bbcc --- /dev/null +++ b/spec/models/deploy_keys_project_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe DeployKeysProject do + describe "Associations" do + it { should belong_to(:deploy_key) } + it { should belong_to(:project) } + end + + describe "Validation" do + it { should validate_presence_of(:project_id) } + it { should validate_presence_of(:deploy_key_id) } + end +end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index a9ab2f05a34..9ccad18248c 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -17,7 +17,6 @@ require 'spec_helper' describe Key do describe "Associations" do it { should belong_to(:user) } - it { should belong_to(:project) } end describe "Mass assignment" do @@ -37,32 +36,15 @@ describe Key do end context "validation of uniqueness" do + let(:user) { create(:user) } - context "as a deploy key" do - let!(:deploy_key) { create(:deploy_key) } - - it "does not accept the same key twice for a project" do - key = build(:key, project: deploy_key.project) - key.should_not be_valid - end - - it "does not accept the same key for another project" do - key = build(:key, project_id: 0) - key.should_not be_valid - end + it "accepts the key once" do + build(:key, user: user).should be_valid end - context "as a personal key" do - let(:user) { create(:user) } - - it "accepts the key once" do - build(:key, user: user).should be_valid - end - - it "does not accepts the key twice" do - create(:key, user: user) - build(:key, user: user).should_not be_valid - end + it "does not accepts the key twice" do + create(:key, user: user) + build(:key, user: user).should_not be_valid end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b7eb7391072..6b7799f7af7 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -37,7 +37,8 @@ describe Project do it { should have_many(:users_projects).dependent(:destroy) } it { should have_many(:notes).dependent(:destroy) } it { should have_many(:snippets).dependent(:destroy) } - it { should have_many(:deploy_keys).dependent(:destroy) } + it { should have_many(:deploy_keys_projects).dependent(:destroy) } + it { should have_many(:deploy_keys) } it { should have_many(:hooks).dependent(:destroy) } it { should have_many(:protected_branches).dependent(:destroy) } it { should have_one(:forked_project_link).dependent(:destroy) } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 1a93148139e..a3620d0a6a5 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -13,7 +13,6 @@ describe Gitlab::API do let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } - let(:key) { create(:key, project: project) } before { project.team << [user, :reporter] } @@ -636,58 +635,61 @@ describe Gitlab::API do end end - describe "GET /projects/:id/keys" do - it "should return array of ssh keys" do - project.deploy_keys << key - project.save - get api("/projects/#{project.id}/keys", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first['title'].should == key.title - end - end + describe :deploy_keys do + let(:deploy_keys_project) { create(:deploy_keys_project, project: project) } + let(:deploy_key) { deploy_keys_project.deploy_key } - describe "GET /projects/:id/keys/:key_id" do - it "should return a single key" do - project.deploy_keys << key - project.save - get api("/projects/#{project.id}/keys/#{key.id}", user) - response.status.should == 200 - json_response['title'].should == key.title + describe "GET /projects/:id/keys" do + before { deploy_key } + + it "should return array of ssh keys" do + get api("/projects/#{project.id}/keys", user) + response.status.should == 200 + json_response.should be_an Array + json_response.first['title'].should == deploy_key.title + end end - it "should return 404 Not Found with invalid ID" do - get api("/projects/#{project.id}/keys/404", user) - response.status.should == 404 - end - end + describe "GET /projects/:id/keys/:key_id" do + it "should return a single key" do + get api("/projects/#{project.id}/keys/#{deploy_key.id}", user) + response.status.should == 200 + json_response['title'].should == deploy_key.title + end - describe "POST /projects/:id/keys" do - it "should not create an invalid ssh key" do - post api("/projects/#{project.id}/keys", user), { title: "invalid key" } - response.status.should == 404 + it "should return 404 Not Found with invalid ID" do + get api("/projects/#{project.id}/keys/404", user) + response.status.should == 404 + end end - it "should create new ssh key" do - key_attrs = attributes_for :key - expect { - post api("/projects/#{project.id}/keys", user), key_attrs - }.to change{ project.deploy_keys.count }.by(1) - end - end + describe "POST /projects/:id/keys" do + it "should not create an invalid ssh key" do + post api("/projects/#{project.id}/keys", user), { title: "invalid key" } + response.status.should == 404 + end - describe "DELETE /projects/:id/keys/:key_id" do - it "should delete existing key" do - project.deploy_keys << key - project.save - expect { - delete api("/projects/#{project.id}/keys/#{key.id}", user) - }.to change{ project.deploy_keys.count }.by(-1) + it "should create new ssh key" do + key_attrs = attributes_for :key + expect { + post api("/projects/#{project.id}/keys", user), key_attrs + }.to change{ project.deploy_keys.count }.by(1) + end end - it "should return 404 Not Found with invalid ID" do - delete api("/projects/#{project.id}/keys/404", user) - response.status.should == 404 + describe "DELETE /projects/:id/keys/:key_id" do + before { deploy_key } + + it "should delete existing key" do + expect { + delete api("/projects/#{project.id}/keys/#{deploy_key.id}", user) + }.to change{ project.deploy_keys.count }.by(-1) + end + + it "should return 404 Not Found with invalid ID" do + delete api("/projects/#{project.id}/keys/404", user) + response.status.should == 404 + end end end end