Skip to content

Commit

Permalink
Add Git sync functionality to scripts dir
Browse files Browse the repository at this point in the history
  • Loading branch information
spajus committed Sep 8, 2013
1 parent c48d1be commit 8efd2c8
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 18 deletions.
55 changes: 55 additions & 0 deletions app/controllers/scripts_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class ScriptsController < ApplicationController

def index
@changed_files = git.changed_files
@scripts = Script.list
end

def create
Expand All @@ -18,6 +20,51 @@ def edit
update_script
end

def git_sync
if repo = params[:repo]
git.repo = repo
git.user_name = params[:user_name]
git.user_email = params[:user_email]
git.init
git.save
flash[:success] = "Will use #{repo} to control source code of your scripts"
end
redirect_to scripts_path
end

def git_pull
if response = git.pull
git_flash('Git pull success:', response)
else
flash[:error] = "Could not pull. Try committing your changes or inspect the repo at #{git.repo_dir} for merge conflicts or errors."
end
redirect_to scripts_path
end

def git_commit
if msg = params[:message]
description = params[:description].try(:strip)
msg = "#{msg}\n#{description}" if description.present?
git.commit(msg)
flash[:success] = 'Git commit added and synced with remote repo'
else
flash[:error] = 'Please provide a commit message'
end
redirect_to scripts_path
end

def git_reset
git.reset
flash[:success] = 'Reverted local changes'
redirect_to scripts_path
end

def git_unlink
GitSync.for_scripts.destroy
flash[:success] = 'Unlinked git repo'
redirect_to scripts_path
end

def destroy
filename = params[:script]
@script = Script.new(filename).delete
Expand All @@ -27,6 +74,14 @@ def destroy

private

def git_flash(msg, response)
flash[:success] = "<p>#{msg}</p><pre>#{response}</pre>".html_safe
end

def git
@git_sync ||= GitSync.for_scripts
end

def update_script
return unless request.post?

Expand Down
82 changes: 74 additions & 8 deletions app/models/git_sync.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,62 @@
class GitSync < ActiveRecord::Base

after_destroy :remove_repo
before_save :update_repo

def self.for_scripts
where(target: 'scripts').first_or_initialize
end

def init
Git.clone(repo, repo_name, path: repo_parent_dir)
return if cloned?
if can_clone?
Git.clone(repo, repo_name, path: repo_parent_dir)
else
temp = File.join(Dir.tmpdir, 'script_migrate')
FileUtils.mkdir_p(temp)
Shell.system "mv -f #{repo_dir}/* #{temp}"
Git.clone(repo, repo_name, path: repo_parent_dir)
Shell.system "mv -f #{temp}/* #{repo_dir}"
Shell.system "rm -rf #{temp}"
end
end

def fetch
g = Git.open(repo_dir)
g.fetch
def status
git.status
rescue => e
Rails.logger.error(e)
false
end

def repo_name
Pathname.new(repo_dir).basename.to_s
def changed_files
st = status
if st == false
Dir.entries(repo_dir).reject { |e| e.start_with?('.') }
else
st.changed.keys + st.untracked.keys
end
end

def repo_parent_dir
Pathname.new(repo_dir).dirname.to_s
def commit(message)
pull
git.add(all: true)
git.commit(message)
git.push(:origin, :master)
end

def pull
git.pull
rescue => e
Rails.logger.error(e)
false
end

def reset
git.reset_hard
end

def push
git.push(:origin, :master)
end

def repo_dir
Expand All @@ -31,8 +67,38 @@ def repo_dir
end
end

private

def can_clone?
(Dir.entries(repo_dir) - ['.', '..']).empty?
end

def cloned?
Dir.exist?(File.join(repo_dir, '.git'))
end

def git
Git.open(repo_dir)
end

def repo_name
Pathname.new(repo_dir).basename.to_s
end

def repo_parent_dir
Pathname.new(repo_dir).dirname.to_s
end

def remove_repo
Shell.system "rm -rf #{repo_dir}/.git"
end

def update_repo
g = Git.open(repo_dir)
g.config('user.name', user_name)
g.config('user.email', user_email)
g.remotes.first.remove
g.add_remote(:origin, repo)
end

end
24 changes: 24 additions & 0 deletions app/views/git_syncs/_commit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<table class="table table-striped table-bordered table-hover">
<tr>
<td width="15%">
<%= label_tag "Message" %>
</td>
<td width="50%">
<%= text_field_tag :message, nil, placeholder: 'Update scripts', maxlength: 70, class: 'input-block-level' %>
</td>
<td>
Short message (~50 chars according to <a href="http://git-scm.com/book/ch5-2.html#Commit-Guidelines">Git Commit Guidelines</a>)
</td>
</tr>
<tr>
<td>
<%= label_tag "Description" %>
</td>
<td>
<%= text_area_tag :full_message, nil, class: 'input-block-level' %>
</td>
<td>
Optional description. Bullet points are ok.
</td>
</tr>
</table>
35 changes: 35 additions & 0 deletions app/views/git_syncs/_fields.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<table class="table table-striped table-bordered table-hover">
<tr>
<td width="15%">
<%= label_tag "Repo" %>
</td>
<td width="50%">
<%= text_field_tag :repo, @git_sync.repo, placeholder: 'git@github.com:foobar/hubot-scripts.git', class: 'input-block-level' %>
</td>
<td>
Git Repository URI
</td>
</tr>
<tr>
<td>
<%= label_tag "User Name" %>
</td>
<td>
<%= text_field_tag :user_name, @git_sync.user_name, placeholder: 'Your Name', class: 'input-block-level' %>
</td>
<td>
git config user.name
</td>
</tr>
<tr>
<td>
<%= label_tag "User Email" %>
</td>
<td>
<%= text_field_tag :user_email, @git_sync.user_email, placeholder: current_user.email, class: 'input-block-level' %>
</td>
<td>
git config user.email
</td>
</tr>
</table>
46 changes: 45 additions & 1 deletion app/views/scripts/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,53 @@ external-scripts.json, which can be found in configuration tabs.</p>
<% end %>
<hr />
<ul class="nav nav-pills">
<% Script.list.each do |script| %>
<% @scripts.each do |script| %>
<li><%= link_to script, edit_scripts_path(script: script) %></li>
<% end %>
</ul>
</div>

<% if @git_sync.persisted? %>
<%= form_tag git_pull_scripts_path do %>
<%= submit_tag 'Git Pull', class: 'btn btn-success js-loader', data: { 'loading-text' => 'Pulling, please wait...' } %>
<% end %>
<% end %>
<% if @git_sync.persisted? && @changed_files.any? %>
<h1>Commit Changes</h1>

<div class="well">
<% @changed_files.each do |changed| %>
<div><i class="icon-lightbulb"></i> <%= link_to changed, edit_scripts_path(script: changed) %></div>
<% end %>
</div>

<%= form_tag git_commit_scripts_path, class: 'form form-inline' do %>
<%= render 'git_syncs/commit' %>
<%= submit_tag 'Commit', class: 'btn btn-success js-loader', data: { 'loading-text' => 'Syncing with remote repo, please wait...' } %>
<%= link_to 'Reset local changes', git_reset_scripts_path, method: 'delete', class: 'btn btn-danger pull-right', confirm: 'Are you sure you want to do `git reset --hard` on your changes?' %>
<% end %>
<% end %>

<div style="clear: both;"></div>

<h1>Git Settings</h1>

<p>Sync your custom Hubot scripts in a separate repo</p>

<%= form_tag git_sync_scripts_path, class: 'form form-inline' do %>
<%= render 'git_syncs/fields' %>
<% if @git_sync.persisted? %>
<%= submit_tag 'Update repo settings', class: 'btn btn-success' %>
<%= link_to 'Unlink repo', git_unlink_scripts_path, method: 'delete', class: 'btn btn-danger pull-right', confirm: 'Are you sure you want to unlink scripts from git repo?' %>
<% else %>
<%= submit_tag 'Set repo', class: 'btn btn-success js-loader', data: { 'loading-text' => 'Linking with git repo, please wait...' } %>
<% end %>
<% end %>

6 changes: 6 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
resource :scripts do
get :index
match :edit, via: [:get, :post]

post :git_sync
post :git_commit
post :git_pull
delete :git_reset
delete :git_unlink
end

end
63 changes: 63 additions & 0 deletions spec/controllers/scripts_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
describe ScriptsController do

let(:user) { create :user }
let(:git) { double(:git) }
before { sign_in :user, user }

describe 'GET #index' do
Expand All @@ -22,4 +23,66 @@

it { should be_success }
end

describe 'POST #git_sync' do
subject { post :git_sync, repo: 'foo@bar.git', user_name: 'foo', user_email: 'foo@foo.bar' }

let(:git) { double(:git) }

before do
Git.should_receive(:open).and_return(git)
git.should_receive(:config).with('user.name', 'foo')
git.should_receive(:config).with('user.email', 'foo@foo.bar')
git.stub_chain(:remotes, :first, :remove)
git.should_receive(:add_remote).with(:origin, 'foo@bar.git')
end

it { should redirect_to scripts_path }
end

describe 'POST #git_pull' do
subject { post :git_pull }

before do
Git.should_receive(:open).and_return(git)
git.should_receive(:pull).and_return('ok')
end

specify do
subject.should redirect_to scripts_path
flash[:success].should include('ok')
end
end

describe 'POST #git_commit' do
subject { post :git_commit, message: 'foo', description: 'bar' }

before { GitSync.any_instance.should_receive(:commit).with("foo\nbar") }

specify do
subject.should redirect_to scripts_path
flash[:success].should be_present
end
end

describe 'DELETE #git_reset' do
subject { delete :git_reset }

before { GitSync.any_instance.should_receive(:reset) }

specify do
subject.should redirect_to scripts_path
flash[:success].should be_present
end
end

describe 'DELETE #git_unlink' do
subject { delete :git_unlink }

specify do
subject.should redirect_to scripts_path
GitSync.for_scripts.should_not be_persisted
flash[:success].should be_present
end
end
end
3 changes: 3 additions & 0 deletions spec/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
end

factory :git_sync do
repo 'git://github.com/foo/bar.git'
user_name 'Foo Bar'
user_email 'foo.bar@example.com'
factory :git_sync_scripts do
target 'scripts'
end
Expand Down
Loading

0 comments on commit 8efd2c8

Please sign in to comment.