Skip to content

Commit

Permalink
Better profile integration: on-disk directories, ssh keys.
Browse files Browse the repository at this point in the history
  • Loading branch information
pwnall committed Jul 14, 2010
1 parent 45e5ab7 commit 35e17c3
Show file tree
Hide file tree
Showing 20 changed files with 253 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
@@ -1,6 +1,6 @@
GIT
remote: git://github.com/rails/rails.git
revision: 7250909
revision: d10ecfe
specs:
actionmailer (3.0.0.beta4)
actionpack (= 3.0.0.beta4)
Expand Down
3 changes: 3 additions & 0 deletions app/helpers/profiles_helper.rb
@@ -1,2 +1,5 @@
module ProfilesHelper
def profile_select_options
Profile.all.map { |p| [p.name, p.id] }
end
end
56 changes: 56 additions & 0 deletions app/models/profile.rb
Expand Up @@ -6,4 +6,60 @@ class Profile < ActiveRecord::Base

# The profile's long name.
validates :display_name, :length => 1..256, :presence => true

# The location of the profile's repositories on disk.
def local_path
self.class.local_path name
end

# The location of a profile's repositories on disk.
#
# Args:
# name:: the repository's name
def self.local_path(name)
File.join '/home', ConfigFlag['git_user'], 'repos', name
end
end

# :nodoc: keep on-disk user directories synchronized
class Profile
after_create :create_profile_directory
before_save :save_old_profile_name
after_update :relocate_profile_directory
after_destroy :delete_profile_directory

# Creates a directory for the user's repositories on disk.
def create_profile_directory
# TODO: background job.
FileUtils.mkdir_p local_path
FileUtils.chmod_R 0770, local_path
local_path
end

# Relocates a Git repository on disk.
def self.relocate_profile_directory(old_name, new_name)
# TODO: maybe this should be a background job.
old_path = local_path old_name
new_path = local_path new_name
FileUtils.mv old_path, new_path
end

# Saves the profile's old name, so it can be relocated.
def save_old_profile_name
@_old_profile_name = name_change.first
end

# Relocates the profile's disk directory, after the model's name is changed.
def relocate_profile_directory
old_name = @_old_profile_name

return if name == old_name
self.class.relocate_profile_directory old_name, name
end

# Deletes the on-disk repository.
def delete_profile_directory
# TODO: background job.
FileUtils.rm_r local_path
end
end
17 changes: 8 additions & 9 deletions app/models/repository.rb
Expand Up @@ -10,17 +10,16 @@ class Repository < ActiveRecord::Base

# The repository's location on disk.
def local_path
self.class.local_path profile.name, name
self.class.local_path profile, name
end

# The on-disk location of a repository.
#
# Args:
# profile_name:: the name of the profile owning the repository
# profile:: the profile owning the repository
# name:: the repository's name
def self.local_path(profile_name, name)
File.join '/home', ConfigFlag['git_user'], 'repos', profile_name,
name + '.git'
def self.local_path(profile, name)
File.join profile.local_path, name + '.git'
end

# The repository's URL for SSH access.
Expand Down Expand Up @@ -53,10 +52,10 @@ def create_local_repository
end

# Relocates a Git repository on disk.
def self.relocate_local_repository(profile_name, old_name, new_name)
def self.relocate_local_repository(profile, old_name, new_name)
# TODO: maybe this should be a background job.
old_path = local_path profile_name, old_name
new_path = local_path profile_name, new_name
old_path = local_path profile, old_name
new_path = local_path profile, new_name
FileUtils.mv old_path, new_path
end

Expand All @@ -70,7 +69,7 @@ def relocate_local_repository
old_name = @_old_repository_name

return if name == old_name
self.class.relocate_local_repository profile.name, old_name, name
self.class.relocate_local_repository profile, old_name, name
@grit_repo = nil
end

Expand Down
4 changes: 4 additions & 0 deletions app/models/ssh_key.rb
@@ -1,5 +1,9 @@
# Public SSL key used to connect to the repositories via git+ssh.
class SshKey < ActiveRecord::Base
# The profile that uses the SSH key to authenticate.
belongs_to :profile
validates :profile, :presence => true

# The key's SSH fingerprint.
validates :fprint, :presence => true, :length => 1..128, :uniqueness => true
# A user-friendly name for the key.
Expand Down
25 changes: 25 additions & 0 deletions app/views/profiles/_form.html.erb
@@ -0,0 +1,25 @@
<%= form_for(@profile) do |f| %>
<% if @profile.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@profile.errors.count, "error") %> prohibited this profile from being saved:</h2>

<ul>
<% @profile.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :display_name %><br />
<%= f.text_field :display_name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
6 changes: 6 additions & 0 deletions app/views/profiles/edit.html.erb
@@ -0,0 +1,6 @@
<h1>Editing profile</h1>

<%= render 'form' %>
<%= link_to 'Show', @profile %> |
<%= link_to 'Back', profiles_path %>
23 changes: 23 additions & 0 deletions app/views/profiles/index.html.erb
@@ -0,0 +1,23 @@
<h1>Listing profiles</h1>

<table>
<tr>
<th>Name</th>
<th></th>
<th></th>
<th></th>
</tr>

<% @profiles.each do |profile| %>
<tr>
<td><%= profile.name %></td>
<td><%= link_to 'Show', profile %></td>
<td><%= link_to 'Edit', edit_profile_path(profile) %></td>
<td><%= link_to 'Destroy', profile, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>

<br />

<%= link_to 'New Profile', new_profile_path %>
5 changes: 5 additions & 0 deletions app/views/profiles/new.html.erb
@@ -0,0 +1,5 @@
<h1>New profile</h1>

<%= render 'form' %>
<%= link_to 'Back', profiles_path %>
10 changes: 10 additions & 0 deletions app/views/profiles/show.html.erb
@@ -0,0 +1,10 @@
<p id="notice"><%= notice %></p>

<p>
<b>Name:</b>
<%= @profile.name %>
</p>


<%= link_to 'Edit', edit_profile_path(@profile) %> |
<%= link_to 'Back', profiles_path %>
4 changes: 4 additions & 0 deletions app/views/repositories/_form.html.erb
Expand Up @@ -11,6 +11,10 @@
</div>
<% end %>

<div class="field">
<%= f.label :profile_id, 'Profile' %><br />
<%= f.select :profile_id, profile_select_options %>
</div>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
Expand Down
4 changes: 4 additions & 0 deletions app/views/ssh_keys/_form.html.erb
Expand Up @@ -11,6 +11,10 @@
</div>
<% end %>

<div class="field">
<%= f.label :profile_id, 'Profile' %><br />
<%= f.select :profile_id, profile_select_options %>
</div>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
Expand Down
6 changes: 6 additions & 0 deletions config/routes.rb
@@ -1,4 +1,10 @@
Dexter::Application.routes.draw do
resources :profiles

resources :commits

resources :branches

resources :ssh_keys

resources :repositories
Expand Down
2 changes: 2 additions & 0 deletions db/migrate/20100711203913_create_ssh_keys.rb
Expand Up @@ -2,12 +2,14 @@ class CreateSshKeys < ActiveRecord::Migration
def self.up
create_table :ssh_keys do |t|
t.string :fprint, :limit => 128, :null => false
t.integer :profile_id, :null => false
t.string :name, :limit => 128, :null => false
t.text :key_line, :limit => 1.kilobyte, :null => false

t.timestamps
end
add_index :ssh_keys, :fprint, :unique => true, :null => false
add_index :ssh_keys, :profile_id, :unique => false
end

def self.down
Expand Down
53 changes: 48 additions & 5 deletions db/schema.rb
Expand Up @@ -10,7 +10,31 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20100711203913) do
ActiveRecord::Schema.define(:version => 20100714032151) do

create_table "branches", :force => true do |t|
t.string "name", :limit => 128, :null => false
t.integer "repository_id", :null => false
end

create_table "commit_parents", :force => true do |t|
t.integer "commit_id", :null => false
t.integer "parent_id", :null => false
end

add_index "commit_parents", ["commit_id", "parent_id"], :name => "index_commit_parents_on_commit_id_and_parent_id", :unique => true

create_table "commits", :force => true do |t|
t.integer "repository_id", :null => false
t.string "gitid", :limit => 64, :null => false
t.integer "tree_id", :null => false
t.string "author", :limit => 256, :null => false
t.string "committer", :limit => 256, :null => false
t.datetime "author_time", :null => false
t.datetime "committer_time", :null => false
end

add_index "commits", ["repository_id", "gitid"], :name => "index_commits_on_repository_id_and_gitid", :unique => true

create_table "config_flags", :force => true do |t|
t.string "name", :null => false
Expand All @@ -19,22 +43,41 @@

add_index "config_flags", ["name"], :name => "index_config_flags_on_name", :unique => true

create_table "profiles", :force => true do |t|
t.string "name", :limit => 32, :null => false
t.string "display_name", :limit => 128, :null => false
t.datetime "created_at"
t.datetime "updated_at"
end

add_index "profiles", ["name"], :name => "index_profiles_on_name", :unique => true

create_table "repositories", :force => true do |t|
t.integer "profile_id", :null => false
t.string "name", :limit => 64, :null => false
t.datetime "created_at"
t.datetime "updated_at"
end

add_index "repositories", ["name"], :name => "index_repositories_on_name", :unique => true
add_index "repositories", ["profile_id", "name"], :name => "index_repositories_on_profile_id_and_name", :unique => true

create_table "ssh_keys", :force => true do |t|
t.string "fprint", :limit => 128, :null => false
t.string "name", :limit => 128, :null => false
t.text "key_line", :limit => 1024, :null => false
t.string "fprint", :limit => 128, :null => false
t.integer "profile_id", :null => false
t.string "name", :limit => 128, :null => false
t.text "key_line", :null => false
t.datetime "created_at"
t.datetime "updated_at"
end

add_index "ssh_keys", ["fprint"], :name => "index_ssh_keys_on_fprint", :unique => true
add_index "ssh_keys", ["profile_id"], :name => "index_ssh_keys_on_profile_id"

create_table "trees", :force => true do |t|
t.integer "repository_id"
t.string "gitid", :limit => 64, :null => false
end

add_index "trees", ["repository_id", "gitid"], :name => "index_trees_on_repository_id_and_gitid", :unique => true

end
2 changes: 2 additions & 0 deletions test/fixtures/ssh_keys.yml
@@ -1,11 +1,13 @@
<% _rsa_key = File.read('test/fixtures/ssh_keys/id_rsa.pub').strip %>
rsa:
profile: dexter
name: Test RSA Key
key_line: <%= _rsa_key.inspect %>
fprint: <%= SshKey.fingerprint(_rsa_key).inspect %>

<% _dsa_key = File.read('test/fixtures/ssh_keys/id_dsa.pub').strip %>
dsa:
profile: costan
name: Test DSA Key
key_line: <%= _dsa_key.inspect %>
fprint: <%= SshKey.fingerprint(_dsa_key).inspect %>
3 changes: 3 additions & 0 deletions test/test_helper.rb
Expand Up @@ -11,3 +11,6 @@ class ActiveSupport::TestCase

# Add more helper methods to be used by all tests here...
end

require File.expand_path('../helpers/mock_profile_paths.rb', __FILE__)
require File.expand_path('../helpers/mock_profile_paths.rb', __FILE__)
28 changes: 26 additions & 2 deletions test/unit/profile_test.rb
@@ -1,15 +1,23 @@
require 'test_helper'

class ProfileTest < ActiveSupport::TestCase
setup :mock_profile_paths

def setup
@profile = Profile.new :name => 'awesome',
:display_name => 'Awesome Profile'
end

test 'local_path' do
mock_profile_paths_undo

assert_equal '/home/git-test/repos/awesome', @profile.local_path
end

test 'setup' do
assert @profile.valid?
end

test 'name has to be unique' do
@profile.name = profiles(:costan).name
assert !@profile.valid?
Expand All @@ -25,5 +33,21 @@ def setup
test 'display name has to be non-nil' do
@profile.display_name = nil
assert !@profile.valid?
end
end

test 'model-directory lifetime sync' do
@profile.save!
assert File.exist?('tmp/test_git_root/awesome'),
'Profile directory not created on disk'

@profile.update_attributes! :name => 'pwnage'
assert !File.exist?('tmp/test_git_root/awesome'),
'Old directory not deleted on rename'
assert File.exist?('tmp/test_git_root/pwnage'),
'New directory not created on rename'

@profile.destroy
assert !File.exist?('tmp/test_git_root/pwnage'),
'Old directory not deleted on rename'
end
end

0 comments on commit 35e17c3

Please sign in to comment.