From 2346cfe1ebfbf43cab013a82bca127e744eef8ea Mon Sep 17 00:00:00 2001 From: Joel Moss Date: Tue, 13 Dec 2011 11:03:33 +0000 Subject: [PATCH] Cloning repos --- Gemfile | 4 ++ Gemfile.lock | 24 ++++++++-- README.rdoc | 11 +++++ app/assets/javascripts/bootstrap.js.coffee | 5 ++- app/controllers/dashboard_controller.rb | 1 + app/controllers/projects_controller.rb | 10 +++++ app/models/project.rb | 14 ++++++ app/models/user.rb | 6 +-- app/views/dashboard/_projects.html.erb | 18 ++++++++ app/views/dashboard/_repo.html.erb | 6 ++- app/views/dashboard/_repos.html.erb | 19 ++++++++ app/views/dashboard/index.html.erb | 26 ++++------- app/views/projects/_project.html.erb | 3 ++ app/views/projects/show.html.erb | 3 ++ config/application.rb | 4 +- config/initializers/girl_friday.rb | 9 ++++ config/routes.rb | 4 +- db/migrate/20111210145824_create_projects.rb | 10 +++++ db/schema.rb | 9 +++- lib/strano.rb | 4 ++ lib/strano/repo.rb | 39 ++++++++++++++++ spec/factories/repos.rb | 7 +++ spec/lib/strano/repo_spec.rb | 47 ++++++++++++++++++++ spec/models/project_spec.rb | 5 +++ spec/spec_helper.rb | 2 + vendor/repos | 1 + 26 files changed, 260 insertions(+), 31 deletions(-) create mode 100644 app/controllers/projects_controller.rb create mode 100644 app/models/project.rb create mode 100644 app/views/dashboard/_projects.html.erb create mode 100644 app/views/dashboard/_repos.html.erb create mode 100644 app/views/projects/_project.html.erb create mode 100644 app/views/projects/show.html.erb create mode 100644 config/initializers/girl_friday.rb create mode 100644 db/migrate/20111210145824_create_projects.rb create mode 100644 lib/strano.rb create mode 100644 lib/strano/repo.rb create mode 100644 spec/factories/repos.rb create mode 100644 spec/lib/strano/repo_spec.rb create mode 100644 spec/models/project_spec.rb create mode 120000 vendor/repos diff --git a/Gemfile b/Gemfile index c43fabe..e260f4b 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,9 @@ gem 'devise' gem 'omniauth-github' gem 'github_api' gem 'capistrano' +gem 'grit' +gem 'girl_friday' +gem 'inherited_resources' # To use debugger # gem 'ruby-debug19', :require => 'ruby-debug' @@ -41,4 +44,5 @@ end group :test do gem 'sqlite3' gem 'webmock' + gem "fakefs", :require => "fakefs/safe" end \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 7a5a7d6..49c13ab 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -48,6 +48,7 @@ GEM coffee-script-source (1.1.3) commonjs (0.2.0) therubyracer (~> 0.9.9) + connection_pool (0.1.0) crack (0.3.1) database_cleaner (0.7.0) devise (1.5.2) @@ -56,23 +57,29 @@ GEM warden (~> 1.1) diff-lcs (1.1.3) erubis (2.7.0) - execjs (1.2.11) + execjs (1.2.12) multi_json (~> 1.0) factory_girl (2.3.2) activesupport factory_girl_rails (1.4.0) factory_girl (~> 2.3.0) railties (>= 3.0.0) + fakefs (0.4.0) faraday (0.7.5) addressable (~> 2.2.6) multipart-post (~> 1.1.3) rack (>= 1.1.0, < 2) ffaker (1.10.1) + girl_friday (0.9.7) + connection_pool (~> 0.1.0) github_api (0.3.3) faraday (~> 0.7.4) hashie (~> 1.2.0) multi_json (~> 1.0.3) oauth2 (~> 0.5.0) + grit (2.4.1) + diff-lcs (~> 1.1) + mime-types (~> 1.15) growl (1.0.3) guard (0.8.8) thor (~> 0.14.6) @@ -81,12 +88,16 @@ GEM guard (>= 0.2.2) guard-pow (0.2.1) guard (>= 0.3.0) - guard-rspec (0.5.8) + guard-rspec (0.5.9) guard (>= 0.8.4) + has_scope (0.5.1) hashie (1.2.0) highline (1.6.8) hike (1.2.1) i18n (0.6.0) + inherited_resources (1.3.0) + has_scope (~> 0.5.0) + responders (~> 0.6.0) jquery-rails (1.0.19) railties (~> 3.0) thor (~> 0.14) @@ -106,7 +117,7 @@ GEM mime-types (1.17.2) multi_json (1.0.4) multipart-post (1.1.4) - mysql2 (0.3.10) + mysql2 (0.3.11) net-scp (1.0.4) net-ssh (>= 1.99.1) net-sftp (2.0.5) @@ -156,6 +167,7 @@ GEM rb-fsevent (0.4.3.1) rdoc (3.11) json (~> 1.4) + responders (0.6.4) rspec (2.7.0) rspec-core (~> 2.7.0) rspec-expectations (~> 2.7.0) @@ -188,7 +200,7 @@ GEM treetop (1.4.10) polyglot polyglot (>= 0.3.1) - twitter-bootstrap-rails (1.4.1) + twitter-bootstrap-rails (1.4.2) actionpack jquery-rails (~> 1.0) less @@ -214,13 +226,17 @@ DEPENDENCIES database_cleaner devise factory_girl_rails + fakefs ffaker + girl_friday github_api + grit growl guard guard-bundler guard-pow guard-rspec + inherited_resources jquery-rails marked mysql2 diff --git a/README.rdoc b/README.rdoc index 653aa5f..14756e3 100644 --- a/README.rdoc +++ b/README.rdoc @@ -14,7 +14,18 @@ The Capistrano backed deployment management UI. - Git pull to the local repo each time the project is accessed and/or on a schedule +==== Setup a repo + +- + + + === Public SSH Key Save your server's public SSH key to ENV['STRANO_PUBLIC_SSH_KEY'] + + +=== Repository clone location + +In vendor/repos, but can be changed by setting ENV['STRANO_CLONE_PATH'] \ No newline at end of file diff --git a/app/assets/javascripts/bootstrap.js.coffee b/app/assets/javascripts/bootstrap.js.coffee index 9b33aad..06c93fd 100644 --- a/app/assets/javascripts/bootstrap.js.coffee +++ b/app/assets/javascripts/bootstrap.js.coffee @@ -1,13 +1,16 @@ $ -> $("a[rel=twipsy]").twipsy live: true + $ -> $("a[rel=popover]").popover offset: 10 + $ -> domModal = $(".modal").modal( backdrop: true closeOnEscape: true ) $(".open-modal").click -> - domModal.toggle() + domModal.toggle() + $ -> $(".btn").button "complete" diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 61ee3ff..2c4a59c 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -3,5 +3,6 @@ class DashboardController < ApplicationController def index @repos = Github::Repos.new(:oauth_token => current_user.github_access_token).repos + @projects = Project.all end end \ No newline at end of file diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb new file mode 100644 index 0000000..68d1867 --- /dev/null +++ b/app/controllers/projects_controller.rb @@ -0,0 +1,10 @@ +class ProjectsController < InheritedResources::Base + + + protected + + def begin_of_association_chain + current_user + end + +end \ No newline at end of file diff --git a/app/models/project.rb b/app/models/project.rb new file mode 100644 index 0000000..de9b2e8 --- /dev/null +++ b/app/models/project.rb @@ -0,0 +1,14 @@ +class Project < ActiveRecord::Base + + belongs_to :user + + after_create :clone_repo + + + private + + def clone_repo + CLONE_QUEUE << url + end + +end diff --git a/app/models/user.rb b/app/models/user.rb index 481712d..33cad17 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,6 @@ class User < ActiveRecord::Base - # Include default devise modules. Others available are: - # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable + has_many :projects + devise :omniauthable cattr_accessor :disable_ssh_github_upload @@ -51,7 +51,7 @@ def authorized_for_github? # Upload SSH key to github, but not if disable_ssh_github_upload is true. # User must also be authorized for Github API access. def upload_ssh_key_to_github - if !self.class.disable_ssh_github_upload && authorized_for_github? + if !ssh_key_uploaded_to_github? && !self.class.disable_ssh_github_upload && authorized_for_github? github.create_key(:title => "Strano", :key => ENV['STRANO_PUBLIC_SSH_KEY']) self.toggle! :ssh_key_uploaded_to_github end diff --git a/app/views/dashboard/_projects.html.erb b/app/views/dashboard/_projects.html.erb new file mode 100644 index 0000000..2f3fc86 --- /dev/null +++ b/app/views/dashboard/_projects.html.erb @@ -0,0 +1,18 @@ +<%- if @projects.empty? -%> + +

You haven't created any projects yet! Choose a repository to get started...

+ +<% else %> + + + + + + + + + <%= render @projects %> + +
Name
+ +<%- end -%> \ No newline at end of file diff --git a/app/views/dashboard/_repo.html.erb b/app/views/dashboard/_repo.html.erb index 806d186..c7a4302 100644 --- a/app/views/dashboard/_repo.html.erb +++ b/app/views/dashboard/_repo.html.erb @@ -1,4 +1,6 @@ +<%- return if Project.exists?(:url => repo.ssh_url) -%> + - <%= link_to 'Setup', '#', :class => "btn primary" %> + <%= link_to 'Setup', projects_path('project[url]' => repo.ssh_url), :class => "btn primary", :method => :post %>
<%= link_to repo.name, repo.html_url %>
- + \ No newline at end of file diff --git a/app/views/dashboard/_repos.html.erb b/app/views/dashboard/_repos.html.erb new file mode 100644 index 0000000..0434dbb --- /dev/null +++ b/app/views/dashboard/_repos.html.erb @@ -0,0 +1,19 @@ +<%- if @repos.empty? -%> + +

No repositories found in your Github account! Strano currently only supports the deployment of Github repositories.

+ +<% else %> + + + + + + + + + + <%= render :partial => 'repo', :collection => @repos %> + +
Name
+ +<%- end -%> \ No newline at end of file diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb index db3f25c..7137019 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -1,21 +1,11 @@ <%- title 'Dashboard' -%> -<%- if @repos.empty? -%> - -

No repositories found in your Github account!

- -<% else %> + - - - - - - - - - <%= render :partial => 'repo', :collection => @repos %> - -
Name
- -<%- end -%> \ No newline at end of file +
+
<%= render 'projects' %>
+
<%= render 'repos' %>
+
\ No newline at end of file diff --git a/app/views/projects/_project.html.erb b/app/views/projects/_project.html.erb new file mode 100644 index 0000000..4fcb4d3 --- /dev/null +++ b/app/views/projects/_project.html.erb @@ -0,0 +1,3 @@ + + <%= link_to project.url %> + \ No newline at end of file diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb new file mode 100644 index 0000000..c98abd7 --- /dev/null +++ b/app/views/projects/show.html.erb @@ -0,0 +1,3 @@ +<%- title "Project: ##{resource.id}" -%> + + diff --git a/config/application.rb b/config/application.rb index 788c78a..aa004f6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -22,7 +22,7 @@ class Application < Rails::Application # -- all .rb files in that directory are automatically loaded. # Custom directories with classes and modules you want to be autoloadable. - # config.autoload_paths += %W(#{config.root}/extras) + config.autoload_paths += %W(#{config.root}/lib) # Only load the plugins named here, in the order given (default is alphabetical). # :all can be used as a placeholder for all plugins not explicitly named. @@ -50,5 +50,7 @@ class Application < Rails::Application # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' + + config.threadsafe! end end diff --git a/config/initializers/girl_friday.rb b/config/initializers/girl_friday.rb new file mode 100644 index 0000000..b3957f1 --- /dev/null +++ b/config/initializers/girl_friday.rb @@ -0,0 +1,9 @@ +class MyErrorHandler + def handle(ex) + puts ex.message + end +end + +CLONE_QUEUE = GirlFriday::WorkQueue.new(:git_clone, :error_handler => MyErrorHandler) do |msg| + Strano::Repo.clone(msg) +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 8a0dd11..d1d05d4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,7 +4,9 @@ get 'sign_in', :to => 'users/sessions#new', :as => :new_user_session delete 'sign_out', :to => 'users/sessions#destroy', :as => :destroy_user_session end - + + resources :projects + root :to => "dashboard#index" end diff --git a/db/migrate/20111210145824_create_projects.rb b/db/migrate/20111210145824_create_projects.rb new file mode 100644 index 0000000..9555133 --- /dev/null +++ b/db/migrate/20111210145824_create_projects.rb @@ -0,0 +1,10 @@ +class CreateProjects < ActiveRecord::Migration + def change + create_table :projects do |t| + t.string :url + t.references :user + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index dd75e43..bf5f55d 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 => 20111206203808) do +ActiveRecord::Schema.define(:version => 20111210145824) do + + create_table "projects", :force => true do |t| + t.string "github_url" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end create_table "users", :force => true do |t| t.string "email" diff --git a/lib/strano.rb b/lib/strano.rb new file mode 100644 index 0000000..e067eed --- /dev/null +++ b/lib/strano.rb @@ -0,0 +1,4 @@ +module Strano + + +end \ No newline at end of file diff --git a/lib/strano/repo.rb b/lib/strano/repo.rb new file mode 100644 index 0000000..bcd9931 --- /dev/null +++ b/lib/strano/repo.rb @@ -0,0 +1,39 @@ +module Strano + class Repo + + attr_accessor :ssh_url, :user_name, :repo_name + + # Initialize a local repository. + # + # url - The SSH URL of the git repository that this local repo is cloned from. + def initialize(url) + @ssh_url = url + @user_name, @repo_name = url.scan(/.+:(?:(.+)\/(.+))\.git$/).flatten + end + + # Clone a remote repo at the given url. + # + # url - The SSH URL of the git repository that this local repo is cloned from. + # + # Returns the newly clone Strano::Repo object. + def self.clone(url) + repo = new(url) + repo.git.fs_mkdir('..') + repo.git.clone({}, url, repo.path) + repo + end + + def root_path + ENV['STRANO_CLONE_PATH'] || Rails.root.join("vendor/repos") + end + + def path + File.join(root_path, user_name, repo_name) + end + + def git + @git ||= Grit::Git.new(path) + end + + end +end \ No newline at end of file diff --git a/spec/factories/repos.rb b/spec/factories/repos.rb new file mode 100644 index 0000000..8c336f1 --- /dev/null +++ b/spec/factories/repos.rb @@ -0,0 +1,7 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :repo do + github_url "MyString" + end +end diff --git a/spec/lib/strano/repo_spec.rb b/spec/lib/strano/repo_spec.rb new file mode 100644 index 0000000..346fba8 --- /dev/null +++ b/spec/lib/strano/repo_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe Strano::Repo do + + include FakeFS::SpecHelpers + + ENV['STRANO_CLONE_PATH'] = '/test/path/to/repos' + + let(:repo) { Strano::Repo.new('git@github.com:joelmoss/strano.git') } + + describe "#ssh_url" do + it "should be extracted from repo URL" do + repo.ssh_url.should == 'git@github.com:joelmoss/strano.git' + end + end + + describe "#user_name" do + it "should be extracted from repo URL" do + repo.user_name.should == 'joelmoss' + end + end + + describe "#repo_name" do + it "should be extracted from repo URL" do + repo.repo_name.should == 'strano' + end + end + + describe "#path" do + it { repo.path.should == '/test/path/to/repos/joelmoss/strano' } + end + + describe "#git" do + it { repo.git.should be_a(Grit::Git) } + end + + describe ".clone" do + let(:url) { 'git@github.com:joelmoss/strano.git' } + let(:path) { '/test/path/to/repos/joelmoss/strano' } + + it "should clone a github repository to STRANO_CLONE_PATH" do + Grit::Git.any_instance.should_receive(:clone).with({}, url, path) + Strano::Repo.clone(url).should be_a(Strano::Repo) + end + end + +end \ No newline at end of file diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb new file mode 100644 index 0000000..9bda4d9 --- /dev/null +++ b/spec/models/project_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Project do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index da3b4de..e661a35 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,6 +3,7 @@ require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'rspec/autorun' +require 'fakefs/spec_helpers' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -10,6 +11,7 @@ OmniAuth.config.test_mode = true ENV['STRANO_PUBLIC_SSH_KEY'] = 'stranoshakey' +GirlFriday::Queue.immediate! RSpec.configure do |config| config.mock_with :rspec diff --git a/vendor/repos b/vendor/repos new file mode 120000 index 0000000..4dcec46 --- /dev/null +++ b/vendor/repos @@ -0,0 +1 @@ +../../repos \ No newline at end of file