alphonse 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,78 @@
1
+ require 'alphonse/connection'
2
+ require 'alphonse/configs/operation'
3
+ require 'alphonse/configs/bundler'
4
+
5
+ module Alphonse
6
+ class Operator
7
+ include Alphonse::Configs::Tasks
8
+ include Alphonse::Configs::Bundler
9
+ attr_reader :config
10
+
11
+ def initialize(config = {})
12
+ @config = config
13
+ end
14
+
15
+ def execute(command)
16
+ hosts.each do |host_connection|
17
+ begin
18
+ Alphonse.logger.info "\e[4mRunning on #{host_connection.connection_string}\e[0m"
19
+
20
+ host_connection.send_and_execute send(command)
21
+
22
+ Alphonse.logger.success "Aaaaaay! All Actions Completed"
23
+ rescue Exception => e
24
+ Alphonse.logger.error "#{e.class.to_s} : #{e.message}\n\n#{e.backtrace.join("\n")}"
25
+ break
26
+ ensure
27
+ host_connection.close
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Create and setup folder on remote server
35
+ def setup
36
+ Alphonse.logger.operation config[:setup_description]
37
+ prerequisite.push config[:setup].map { |task| send task }
38
+ end
39
+
40
+ # First time deploy of app to remote server
41
+ def deploy
42
+ Alphonse.logger.operation config[:deploy_description]
43
+ prerequisite.push config[:deploy].map { |task| send(task) }
44
+ end
45
+
46
+ # Issue bundle command on remote server
47
+ def bundle_update
48
+ Alphonse.logger.operation config[:bundle_description]
49
+ prerequisite.push config[:bundle_update].map { |task| send(task) }
50
+ end
51
+
52
+ # Pull latest on remote server
53
+ def update
54
+ Alphonse.logger.operation config[:update_description]
55
+ prerequisite.push config[:update].map { |task| send(task) }
56
+ end
57
+
58
+ # Restart the app
59
+ def restart
60
+ Alphonse.logger.operation config[:restart_description]
61
+ prerequisite.push config[:restart].map { |task| send(task) }
62
+ end
63
+
64
+ def prerequisite
65
+ [cd_to_path, 'ls -al', 'which ruby']
66
+ end
67
+
68
+ def hosts
69
+ @hosts ||= begin
70
+ host_connections, hosts, user = [], (config[:hosts] rescue nil), (config[:user] rescue nil)
71
+ hosts.each {|host| host_connections << Connection.new(host, user, config) } if hosts && user;
72
+ host_connections
73
+ end
74
+ end
75
+
76
+
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ module Alphonse
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+ PRE = nil
7
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
8
+ end
9
+ end
data/lib/alphonse.rb ADDED
@@ -0,0 +1,46 @@
1
+ require "alphonse/version"
2
+ require "alphonse/logger"
3
+
4
+ module Alphonse
5
+
6
+ # Base class for all Alphonse exceptions
7
+ class AlphonseError < StandardError
8
+ end
9
+
10
+ # When a command(s) return a stderr stream
11
+ class StdError < AlphonseError
12
+ end
13
+
14
+ # When a configuration file is missing
15
+ class NoFonzfileError < AlphonseError
16
+ end
17
+
18
+ # When a host is not defined
19
+ class MissingUserError < AlphonseError
20
+ end
21
+
22
+ # When a host is not defined
23
+ class MissingHostError < AlphonseError
24
+ end
25
+
26
+ # When an operation is not defined
27
+ class MissingOperationError < AlphonseError
28
+ end
29
+
30
+ # When an operation is not defined
31
+ class MissingCommandError < AlphonseError
32
+ end
33
+
34
+ # When an operation is invalid
35
+ class InvalidOperationError < AlphonseError
36
+ end
37
+
38
+ # When a command is not present or formed corrently
39
+ class CliCommandError < AlphonseError
40
+ end
41
+
42
+ # When an argument is not present or well formed
43
+ class CliArgumentError < AlphonseError
44
+ end
45
+
46
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/cli'
5
+
6
+ describe "Cli" do
7
+
8
+ describe "with an operation passed" do
9
+
10
+ before do
11
+ @command = 'deploy'
12
+ @cli = Alphonse::Cli.new(@command.split(' '))
13
+ end
14
+
15
+ it "should determine the operation to run" do
16
+ @cli.command.should == :deploy
17
+ end
18
+
19
+ it "should match the default options" do
20
+ @cli.options.should == { :environment => :production }
21
+ end
22
+
23
+ end
24
+
25
+ describe "with an operation and environment passed" do
26
+
27
+ before do
28
+ @command = 'deploy -e staging'
29
+ @cli = Alphonse::Cli.new(@command.split(' '))
30
+ end
31
+
32
+ it "should determine the operation to run" do
33
+ @cli.command.should == :deploy
34
+ end
35
+
36
+ it "should match the passed options" do
37
+ @cli.options.should == { :environment => :staging }
38
+ end
39
+
40
+ end
41
+
42
+
43
+ describe "with no arguments" do
44
+ it 'should raise an error' do
45
+ lambda { @cli = Alphonse::Cli.new }.should raise_error(Alphonse::CliArgumentError)
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/config'
5
+
6
+ describe "Config" do
7
+
8
+ describe "with specified Fonzfile" do
9
+ before do
10
+ @config = Alphonse::Config.new(:file_name => 'spec/fonzfiles/default', :test_variable => 1)
11
+ end
12
+
13
+ it 'should load the config file' do
14
+ @config.config_loaded?.should be_true
15
+ end
16
+
17
+ it 'should have attributes set' do
18
+ @config[:user].should_not be_nil
19
+ @config[:hosts].should_not be_nil
20
+ end
21
+
22
+ it 'should be able to access given attributes' do
23
+ @config[:test_variable].should equal(1)
24
+ end
25
+
26
+ it 'should set new attributes' do
27
+ @config.set_attr :new_variable => 'new value'
28
+ @config[:new_variable].should == 'new value'
29
+ end
30
+
31
+ it 'should re set an attribute' do
32
+ @config.set_attr :test_variable => 2
33
+ @config[:test_variable].should equal(2)
34
+ end
35
+
36
+ describe 'calling operator' do
37
+ it 'should initialise and return new operator' do
38
+ @config.operator.should_not be_nil
39
+ @config.operator.class.should equal(Alphonse::Operator)
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ describe 'without specified Fonzfile' do
46
+ before do
47
+ @config = Alphonse::Config.new
48
+ end
49
+
50
+ it 'should use default Fonzfile' do
51
+ @config.config_loaded?.should be_true
52
+ end
53
+
54
+ it 'should have attributes set' do
55
+ @config[:user].should_not be_nil
56
+ @config[:hosts].should_not be_nil
57
+ end
58
+
59
+ end
60
+
61
+
62
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/configs/bundler'
5
+
6
+ describe "Configs::Bundler" do
7
+ before do
8
+
9
+ @klass = Class.new do
10
+ include Alphonse::Configs::Bundler
11
+ attr_reader :config
12
+
13
+ def initialize
14
+ @config = {}
15
+ end
16
+
17
+ def config=(hash)
18
+ @config = hash
19
+ end
20
+
21
+ end.new
22
+
23
+ end
24
+
25
+ after do
26
+ @klass = nil
27
+ end
28
+
29
+ describe "with a Gemfile" do
30
+ before do
31
+ @klass.should_receive(:using_bundler?).and_return(true)
32
+ end
33
+
34
+ describe "with ruby_bin_path" do
35
+ before do
36
+ @klass.config = {:ruby_bin_path => "path/to/ruby/"}
37
+ end
38
+
39
+ describe "when calling bundle_install" do
40
+ it "should return command prepended with ruby_bin_path and bundle exec" do
41
+
42
+ @klass.bundle_install.should == "path/to/ruby/bundle install --deployment --without development test"
43
+ end
44
+ end
45
+
46
+ describe "when calling rake command" do
47
+ it "should return rake command prepended with ruby_bin_path and bundle exec" do
48
+ @klass.rake("--tasks").should == "path/to/ruby/bundle exec rake --tasks"
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ describe "without a Gemfile" do
57
+ before do
58
+ @klass.should_receive(:using_bundler?).and_return(false)
59
+ end
60
+
61
+ describe "without ruby_bin_path" do
62
+ before do
63
+ @klass.config = {}
64
+ end
65
+
66
+ describe "when calling bundle_install" do
67
+ it "should return command without prepends" do
68
+ @klass.bundle_install.should be_nil
69
+ end
70
+ end
71
+
72
+ describe "when calling rake command" do
73
+ it "should return rake command without prepends" do
74
+ @klass.rake("--tasks").should == "rake --tasks"
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/configs/operation'
5
+
6
+ describe "Configs::Operation" do
7
+ before do
8
+ @klass = Class.new do
9
+ include Alphonse::Configs::Operation
10
+ include Alphonse::Configs::Tasks
11
+ attr_reader :config
12
+
13
+ def initialize
14
+ @config = {}
15
+ end
16
+
17
+ def config=(hash)
18
+ @config = hash
19
+ end
20
+
21
+ def set_attr(hash)
22
+ config.merge! hash
23
+ end
24
+
25
+ end.new
26
+
27
+ @test_tasks = [:source_shell_rc, :git_pull]
28
+ end
29
+
30
+ after do
31
+ @klass = nil
32
+ end
33
+
34
+ describe "Given operation and tasks" do
35
+ before do
36
+ @klass.operation :test_operation, "Test Operation Deascription"
37
+ @klass.tasks(@test_tasks)
38
+ end
39
+
40
+ it "should set given tasks in operation key of config" do
41
+ @klass.config[:test_operation].should == @test_tasks
42
+ end
43
+
44
+ it "should set given description in config" do
45
+ @klass.config[:test_operation_description].should_not be_nil
46
+ end
47
+
48
+ describe "Calling tasks" do
49
+ it "should return commands as strings" do
50
+ task = @klass.config[:test_operation].first
51
+ @klass.send(task).should == "source ~/.bashrc"
52
+
53
+ task = @klass.config[:test_operation].last
54
+ @klass.send(task).should == ["git checkout master -q", "git pull origin master -q", "git gc --aggressive"]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/configs/setting'
5
+
6
+ describe "Configs::Setting" do
7
+ before do
8
+ @klass = Class.new do
9
+ include Alphonse::Configs::Setting
10
+ attr_reader :config
11
+
12
+ def initialize
13
+ @config = {}
14
+ end
15
+
16
+ def config=(hash)
17
+ @config = hash
18
+ end
19
+
20
+ def set_attr(hash)
21
+ config.merge! hash
22
+ end
23
+
24
+ end.new
25
+ end
26
+
27
+ after do
28
+ @klass = nil
29
+ end
30
+
31
+ describe "with a single given value for an attribute" do
32
+ before do
33
+ @klass.user("user_name")
34
+ end
35
+
36
+ it "should set correct attribute" do
37
+ @klass.config[:user].should == "user_name"
38
+ end
39
+ end
40
+
41
+ describe "with an array of values given for an attribute" do
42
+ before do
43
+ @klass.hosts(["host.address", "alt_host.address"])
44
+ end
45
+
46
+ it "should set attribute with correct values" do
47
+ @klass.config[:hosts].should == ["host.address", "alt_host.address"]
48
+ end
49
+ end
50
+
51
+
52
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/connection'
5
+
6
+ describe "Connection" do
7
+ describe "with host and user" do
8
+ before do
9
+ mock_net_ssh_session!
10
+ @connection = Alphonse::Connection.new("localhost", "username", {})
11
+ end
12
+
13
+ describe "with given commands" do
14
+ it "should send and excute and return array" do
15
+ session.should_receive(:run).and_return(@result)
16
+
17
+ results = @connection.send_and_execute("date")
18
+ results.class.should equal(Array)
19
+ end
20
+
21
+ it "should set host and username" do
22
+ @connection.connection_string.should == "username@localhost"
23
+ end
24
+
25
+ it "should be able to close session" do
26
+ @connection.close.should equal(true)
27
+ end
28
+ end
29
+
30
+ describe "without given commands" do
31
+ it "should return an empty array" do
32
+ result = @connection.send_and_execute()
33
+ result.class.should equal(Array)
34
+ result.empty?.should be_true
35
+ end
36
+ end
37
+
38
+ describe "given invalid command" do
39
+ it "shoould raise Alphonse::MalformedCommandError" do
40
+ lambda { @connection.send_and_execute(12) }.should raise_error(Alphonse::MissingCommandError)
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "without" do
46
+ describe "host" do
47
+ it "should raise Alphonse::MissingHostError" do
48
+ lambda { Alphonse::Connection.new(nil, "username", {}) }.should raise_error(Alphonse::MissingHostError)
49
+ end
50
+ end
51
+
52
+ describe do
53
+ it "should raise Alphonse::MissingUserError" do
54
+ lambda { Alphonse::Connection.new("localhost", nil, {}) }.should raise_error(Alphonse::MissingUserError)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/config'
5
+ require 'alphonse/operator'
6
+
7
+ describe "Operator" do
8
+
9
+ describe "initialised with config" do
10
+ before do
11
+ @config = Alphonse::Config.new(:file_name => 'spec/fonzfiles/default')
12
+ @operator = Alphonse::Operator.new(@config)
13
+ end
14
+
15
+ it "should have array of Connections" do
16
+ @operator.send(:hosts).class.should equal(Array)
17
+ @operator.send(:hosts).first.class.should equal(Alphonse::Connection)
18
+ end
19
+
20
+ describe "with hosts" do
21
+ before do
22
+ mock_connection!
23
+ @operator.stub(:hosts).and_return([@connection])
24
+ end
25
+
26
+ it "should execute setup operation" do
27
+ @operator.execute(:setup).class.should equal(Array)
28
+ end
29
+
30
+ it "should execute deploy operation" do
31
+ @operator.execute(:deploy).class.should equal(Array)
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ user 'remote_user'
2
+ hosts 'stage.example.com'
3
+ path "/fullpath/to/folder"
4
+ git_repo 'git@gitaddress:repository.git'
5
+ branch 'master'
6
+
7
+ operation :setup, 'Setup server' do
8
+ tasks :clone_repository
9
+ end
10
+
11
+ operation :deploy, 'Deploy repository' do
12
+ tasks :install_gems, :setup_database, :restart_app
13
+ end
14
+
15
+ operation :update, 'Update the repository on the server' do
16
+ tasks :update_repository, :install_gems, :update_database, :restart_app
17
+ end
18
+
19
+ operation :restart, 'Restart application' do
20
+ tasks :restart_app
21
+ end
@@ -0,0 +1,37 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+
4
+ require 'alphonse'
5
+ require 'rspec'
6
+
7
+ RSpec.configure do |config|
8
+ # config
9
+ end
10
+
11
+ def mock_connection!
12
+ @connection = double('connection')
13
+ @connection.stub(:send_and_execute).and_return([])
14
+ @connection.stub(:connection_string).and_return("connection_string")
15
+ @connection.stub(:close).and_return(true)
16
+ end
17
+
18
+ def mock_net_ssh_session!
19
+ @result = double('result')
20
+ @result.stub(:failure?).and_return(:false)
21
+ @result.stub(:output).and_return("Output")
22
+ @result.stub(:duration).and_return(100)
23
+ @session = double("session")
24
+ @session.stub(:close).and_return(true)
25
+ @session.stub(:open).and_return(true)
26
+ @session.stub(:run).and_return(@result)
27
+ Net::SSH::Session.stub(:new).and_return(@session)
28
+ end
29
+
30
+
31
+ def connection
32
+ @connection
33
+ end
34
+
35
+ def session
36
+ @session
37
+ end