Commit e5bcdb9afb6b55c058796de3ec7319d5c75027c6

* beginnings of a Task system
* Make SshKey andRepository create tasks on create/destroy

Commit diff

app/models/repository.rb

 
1010 validates_uniqueness_of :name, :scope => :project_id, :case_sensitive => false
1111
1212 before_save :set_as_mainline_if_first
13 after_create :add_user_as_committer, :create_git_repository
13 after_create :add_user_as_committer, :create_new_repos_task
14 after_destroy :create_delete_repos_task
1415
1516 def self.new_by_cloning(other)
1617 new(:parent => other, :project => other.project)
4444 git_backend.create(full_repository_path)
4545 end
4646
47 def delete_git_repository
48 git_backend.delete!(full_repository_path)
49 end
50
4751 def has_commits?
4852 git_backend.repository_has_commits?(full_repository_path)
4953 end
6969 committers << user
7070 end
7171 end
72
73 def create_new_repos_task
74 Task.create!(:target => self, :command => "create_git_repository")
75 end
76
77 def create_delete_repos_task
78 Task.create!(:target => self, :command => "delete_git_repository")
79 end
7280
7381 protected
7482 def set_as_mainline_if_first
toggle raw diff

app/models/ssh_key.rb

 
66 validates_presence_of :user_id, :key
77 validates_format_of :key, :with => SSH_KEY_FORMAT
88
9 before_save :lint_key!
9 before_save :lint_key!
10 after_create :create_new_task
11 # we only allow people to create/destroy keys after_update :create_update_task
12 after_destroy :create_delete_task
1013
1114 def wrapped_key(cols=72)
1215 key.gsub(/(.{1,#{cols}})/, "\\1\n").strip
2020 %Q{command="gitorious #{user.login}",no-port-forwarding,} +
2121 %Q{no-X11-forwarding,no-agent-forwarding,no-pty #{key}} +
2222 %Q{\n### END KEY #{self.id || "nil"} ###}
23 end
23 end
24
25 def add_to_authorized_keys(key_file_class=SshKeyFile)
26 key_file = key_file_class.new
27 key_file.add_key(self.to_key)
28 end
29
30 def delete_from_authorized_keys(key_file_class=SshKeyFile)
31 key_file = key_file_class.new
32 key_file.delete_key(self.to_key)
33 end
34
35 def create_new_task
36 Task.create!(:target => self, :command => "add_to_authorized_keys")
37 end
38
39 def create_delete_task
40 Task.create!(:target => self, :command => "delete_from_authorized_keys")
41 end
2442
2543 protected
2644 def lint_key!
toggle raw diff

app/models/task.rb

 
1class Task < ActiveRecord::Base
2 belongs_to :target, :polymorphic => true
3
4 def self.find_all_to_perform
5 find(:all, :conditions => {:performed => false})
6 end
7
8 def perform!
9 transaction do
10 target.send(command)
11 self.performed = true
12 self.performed_at = Time.now
13 save!
14 end
15 end
16
17end
toggle raw diff

db/migrate/016_create_tasks.rb

 
1class CreateTasks < ActiveRecord::Migration
2 def self.up
3 create_table :tasks do |t|
4 t.integer :target_id
5 t.string :target_type
6 t.string :command
7 t.boolean :performed, :default => false
8 t.datetime :performed_at
9 t.timestamps
10 end
11 add_index :tasks, :target_id
12 add_index :tasks, :target_type
13 add_index :tasks, :performed
14 end
15
16 def self.down
17 drop_table :tasks
18 end
19end
toggle raw diff

db/schema.rb

 
99#
1010# It's strongly recommended to check this file into your version control system.
1111
12ActiveRecord::Schema.define(:version => 15) do
12ActiveRecord::Schema.define(:version => 16) do
1313
1414 create_table "committerships", :force => true do |t|
1515 t.integer "user_id"
8080 t.string "name"
8181 end
8282
83 create_table "tasks", :force => true do |t|
84 t.integer "target_id"
85 t.string "target_type"
86 t.string "command"
87 t.boolean "performed", :default => false
88 t.datetime "performed_at"
89 t.datetime "created_at"
90 t.datetime "updated_at"
91 end
92
93 add_index "tasks", ["target_id"], :name => "index_tasks_on_target_id"
94 add_index "tasks", ["target_type"], :name => "index_tasks_on_target_type"
95 add_index "tasks", ["performed"], :name => "index_tasks_on_performed"
96
8397 create_table "users", :force => true do |t|
8498 t.string "login"
8599 t.string "email"
toggle raw diff

lib/git_backend.rb

 
1212 end
1313 end
1414
15 def delete!(repos_path)
16 FileUtils.rm_rf(repos_path)
17 end
18
1519 def repository_has_commits?(repos_path)
1620 Dir[File.join(repos_path, "refs/heads/*")].size > 0
1721 end
toggle raw diff

lib/mock_git_backend.rb

 
44 true
55 end
66
7 def delete!(repos_path)
8 true
9 end
10
711 def repository_has_commits?(repos_path)
812 false
913 end
toggle raw diff

spec/fixtures/tasks.yml

 
1# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2create_repo:
3 id: 1
4 target_id: 1
5 target_type: Repository
6 command: create_git_repository
7 performed: 0
8
9add_key:
10 id: 2
11 target_id: 1
12 target_type: SshKey
13 command: add_to_authorized_keys
14 performed: 0
toggle raw diff

spec/models/git_backend_spec.rb

 
1919 GitBackend.create(path)
2020 end
2121
22 it "deletes a git repository" do
23 path = @repository.full_repository_path
24 FileUtils.should_receive(:rm_rf).with(path).and_return(true)
25 GitBackend.delete!(path)
26 end
27
2228 it "knows if a repos has commits" do
2329 path = @repository.full_repository_path
2430 dir_mock = mock("Dir mock")
toggle raw diff

spec/models/repository_spec.rb

 
8585 @repository.create_git_repository
8686 end
8787
88 it "creates an repository after save" do
89 @repository.should_receive(:create_git_repository).and_return(true)
90 @repository.save
88 it "deletes a repository" do
89 @repository.git_backend.should_receive(:delete!).with(@repository.full_repository_path).and_return(true)
90 @repository.delete_git_repository
9191 end
9292
9393 it "knows if has commits" do
125125 @repository.add_committer(users(:moe))
126126 users(:moe).can_write_to?(@repository).should == true
127127 end
128
129 it "creates a Task on create and update" do
130 proc{
131 @repository.save!
132 }.should change(Task, :count)
133 task = Task.find(:first, :conditions => ["target_id = ?", @repository.id], :order => "id desc")
134 task.command.should == "create_git_repository"
135 end
136
137 it "creates a Task on destroy" do
138 @repository.save!
139 proc{
140 @repository.destroy
141 }.should change(Task, :count)
142 task = Task.find(:first, :conditions => ["target_id = ?", @repository.id], :order => "id desc")
143 task.command.should == "delete_git_repository"
144 end
128145end
toggle raw diff

spec/models/ssh_key_spec.rb

 
22
33describe SshKey do
44
5 def create_key(opts={})
5 def new_key(opts={})
66 SshKey.new({
77 :user_id => 1,
88 :key => "ssh-rsa bXljYWtkZHlpemltd21vY2NqdGJnaHN2bXFjdG9zbXplaGlpZnZ0a3VyZWFzc2dkanB4aXNxamxieGVib3l6Z3hmb2ZxZW15Y2FrZGR5aXppbXdtb2NjanRiZ2hzdm1xY3Rvc216ZWhpaWZ2dGt1cmVhc3NnZGpweGlzcWpsYnhlYm95emd4Zm9mcWU= foo@example.com",
1010 end
1111
1212 it "should have a valid ssh key" do
13 key = create_key
13 key = new_key
1414 key.key = ""
1515 key.should_not be_valid
1616 key.key = "foo bar@baz"
2828 end
2929
3030 it "should have a user to be valid" do
31 key = create_key
31 key = new_key
3232 key.user_id = nil
3333 key.should_not be_valid
3434
3838 end
3939
4040 it "strips newlines before save" do
41 ssh = create_key(:key => "ssh-rsa bXljYWtkZHlpemltd21vY2NqdGJnaHN2bXFjdG\n9zbXplaGlpZnZ0a3VyZWFzc2dkanB4aXNxamxieGVib3l6Z3hmb2ZxZW15Y2FrZGR5aXppbXdtb2NjanRiZ2hzdm1xY3Rvc216ZWhpaWZ2dGt1cm\nVhc3NnZGpweGlzcWpsYnhlYm95emd4Zm9mcWU= foo@example.com")
41 ssh = new_key(:key => "ssh-rsa bXljYWtkZHlpemltd21vY2NqdGJnaHN2bXFjdG\n9zbXplaGlpZnZ0a3VyZWFzc2dkanB4aXNxamxieGVib3l6Z3hmb2ZxZW15Y2FrZGR5aXppbXdtb2NjanRiZ2hzdm1xY3Rvc216ZWhpaWZ2dGt1cm\nVhc3NnZGpweGlzcWpsYnhlYm95emd4Zm9mcWU= foo@example.com")
4242 ssh.save
4343 ssh.key.should_not include("\n")
4444 end
4545
4646 it "wraps the key at 72 for display" do
47 ssh = create_key
47 ssh = new_key
4848 ssh.wrapped_key.should include("\n")
4949 end
5050
5151 it "returns a proper ssh key with to_key" do
52 ssh_key = create_key
52 ssh_key = new_key
5353 ssh_key.save!
5454 exp_key = %Q{### START KEY #{ssh_key.id} ###\n} +
5555 %Q{command="gitorious #{users(:johan).login}",no-port-forwarding,} +
5757 %Q{\n### END KEY #{ssh_key.id} ###}
5858 ssh_key.to_key.should == exp_key
5959 end
60
61 it "adds itself to the authorized keys file" do
62 ssh_key_file_mock = mock("SshKeyFile mock")
63 ssh_key = new_key
64 ssh_key_file_mock.should_receive(:new).and_return(ssh_key_file_mock)
65 ssh_key_file_mock.should_receive(:add_key).with(ssh_key.to_key).and_return(true)
66 ssh_key.add_to_authorized_keys(ssh_key_file_mock)
67 end
68
69 it "removes itself to the authorized keys file" do
70 ssh_key_file_mock = mock("SshKeyFile mock")
71 ssh_key = new_key
72 ssh_key_file_mock.should_receive(:new).and_return(ssh_key_file_mock)
73 ssh_key_file_mock.should_receive(:delete_key).with(ssh_key.to_key).and_return(true)
74 ssh_key.delete_from_authorized_keys(ssh_key_file_mock)
75 end
76
77 it "creates a Task on create and update" do
78 ssh_key = new_key
79 proc{
80 ssh_key.save!
81 }.should change(Task, :count)
82 task = Task.find(:first, :conditions => ["target_id = ?", ssh_key.id], :order => "id desc")
83 task.command.should == "add_to_authorized_keys"
84 end
85
86 it "creates a Task on destroy" do
87 ssh_key = new_key
88 ssh_key.save!
89 proc{
90 ssh_key.destroy
91 }.should change(Task, :count)
92 task = Task.find(:first, :conditions => ["target_id = ?", ssh_key.id], :order => "id desc")
93 task.command.should == "delete_from_authorized_keys"
94 end
6095end
toggle raw diff

spec/models/task_spec.rb

 
1require File.dirname(__FILE__) + '/../spec_helper'
2
3describe Task do
4 before(:each) do
5 @task = tasks(:create_repo)
6 end
7
8 it "has_valid_associations" do
9 @task.should have_valid_associations
10 end
11
12 it "performs a task" do
13 @task.target.should_receive(@task.command).and_return(true)
14 @task.perform!
15 @task.reload
16 @task.performed?.should == true
17 @task.performed_at.should_not == nil
18 end
19
20 it "finds tasks that needs performin'" do
21 @task.update_attributes(:performed => true)
22 Task.find_all_to_perform.should == [tasks(:add_key)]
23 end
24end
toggle raw diff