alphonse 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 398b75123e0cb97898362fac3068fc5a37400e8b
4
+ data.tar.gz: e1cb01ef1ed109c4ad532291f1da1f1e95325452
5
+ SHA512:
6
+ metadata.gz: eac1048f2ab0ac42171c61e9b8184c19015482d4bda0735dcd32003cfb1d9201987301a2d05224128b1ca571d7265db0a6d226697efc2cfca5aa7154f2cc1855
7
+ data.tar.gz: 03fcd3ecfc1244bd2b38f9e3f0740593a97c736a9176ed7ee563d702c54edf4fa270592cd5425456e75b13429794a1c16ec296d2ec03dafb9e4e281894a34aa6
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ Fonzfile
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ alphonse
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in alphonse.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Simon Jones
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Alphonse
2
+
3
+ Deployment gem
4
+
5
+ ## Installation
6
+
7
+
8
+ ## Usage
9
+
10
+ TODO: Write usage instructions here
11
+
12
+
13
+ ## Note
14
+
15
+ Chances are you will experience issues with PATH variables and rubygems etc. So here's a quick solution:
16
+
17
+ Log in to your deployment server, as a root user, add the following line to /etc/ssh/sshd_config:
18
+
19
+ PermitUserEnvironment yes
20
+
21
+ Don’t forget to restart ssh:
22
+
23
+ /etc/init.d/ssh restart
24
+
25
+ Now, log in as the deployment user, and create ‘~/.ssh/environment’ with the following content:
26
+
27
+ PATH=/usr/local/rvm/gems/ruby-2.0.0-p247/bin:/bin:/usr/local/rvm/rubies/ruby-2.0.0-p247/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
28
+ GEM_HOME=/usr/local/rvm/gems/ruby-2.0.0-p247
29
+
30
+ *The above paths are for your reference only, obviously you need to work them out for your server environment. The only thing you need to make sure is that the GEM_HOME's path matches one from the PATH.*
31
+
32
+ ## Contributing
33
+
34
+ 1. Fork it
35
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
36
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
37
+ 4. Push to the branch (`git push origin my-new-feature`)
38
+ 5. Create new Pull Request
39
+
40
+ ![May the Fonz be with you](http://lovablelabelsblog.com/wp-content/uploads/2010/03/fonz1.jpg)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = FileList['spec/**/*_spec.rb']
8
+ #spec.rspec_opts = '--order random' need to fix some test to make this randow :( bad tests!
9
+ end
10
+
11
+ desc 'Default: Run all specs.'
12
+ task :default => :spec
data/alphonse.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'alphonse/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "alphonse"
8
+ spec.version = Alphonse::Version::STRING
9
+ spec.authors = ["Simon Jones"]
10
+ spec.email = ["spj3rd@googlemail.com"]
11
+ spec.description = "Deploy like The Fonz"
12
+ spec.summary = "Rails deployment made easy."
13
+ spec.homepage = "http://simonjones.github.com/alphonse"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_development_dependency "rspec", "~> 2.14"
25
+ spec.add_development_dependency "pry", "~> 0.9"
26
+
27
+ spec.add_runtime_dependency "net-ssh-session"
28
+ end
data/bin/alphonse ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+
4
+ $:.push File.expand_path("../../lib", __FILE__)
5
+
6
+ require 'alphonse'
7
+ require 'alphonse/cli'
8
+
9
+ Alphonse::Cli.start
@@ -0,0 +1,73 @@
1
+ require 'optparse'
2
+ require 'alphonse/config'
3
+
4
+ module Alphonse
5
+ class Cli
6
+
7
+ attr_reader :command, :options, :argv
8
+ attr_accessor :config
9
+
10
+ def self.start(argv = ARGV)
11
+ cli = self.new(argv)
12
+ cli.execute
13
+ cli
14
+ end
15
+
16
+ def initialize(argv = [])
17
+ @argv = argv
18
+ @options = { :environment => :production }
19
+
20
+ @command = option_parser.parse!(@argv).delete_at(0)
21
+
22
+ if @command
23
+ @command = @command.to_sym
24
+ else
25
+ raise CliArgumentError, "An operation is required"
26
+ end
27
+ end
28
+
29
+ def execute
30
+ case command
31
+ when :init
32
+ Config.init
33
+ else
34
+ config = Config.new(options)
35
+ config.operator.execute(command)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def option_parser
42
+ @option_parser ||= ::OptionParser.new do |op|
43
+ op.banner = 'Usage: alphonse <command> [options]'
44
+ op.separator ''
45
+
46
+ op.on "-e", "--env ENV", "Environment" do |e|
47
+ @options[:environment] = e.to_sym
48
+ end
49
+
50
+ op.on "--verbose", "Verbose" do
51
+ $alphonse_log_level = ::Logger::DEBUG
52
+ @options[:verbose] = true
53
+ end
54
+
55
+ op.on "-f", "--file FONZFILE", "Load a different Fonzfile" do |f|
56
+ @options[:file_name] = f || 'Fonzfile'
57
+ end
58
+
59
+ op.on_tail "-h", "--help", "Help" do
60
+ puts @option_parser
61
+ exit
62
+ end
63
+
64
+ op.on_tail "-v", "--version", "Show version" do
65
+ puts "Alphonse v#{Alphonse::Version::STRING}"
66
+ exit
67
+ end
68
+
69
+ end
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,70 @@
1
+ require 'alphonse/operator'
2
+ require 'alphonse/configs/setting'
3
+ require 'alphonse/configs/operation'
4
+
5
+ module Alphonse
6
+ class Config < Hash
7
+ include Alphonse::Configs::Setting
8
+ include Alphonse::Configs::Operation
9
+
10
+ FONZFILE = "./Fonzfile"
11
+ attr_reader :loaded
12
+
13
+ # Initialise the Fonzfile
14
+ def self.init
15
+ puts "Fonzie Initialise!"
16
+
17
+ pwd = File.dirname(__FILE__)
18
+ if RUBY_VERSION == /1.8/
19
+ require 'ftools'
20
+ File.copy("#{pwd}/default/fonzfile.rb", FONZFILE)
21
+ else
22
+ require 'fileutils'
23
+ ::FileUtils.copy("#{pwd}/default/fonzfile.rb", FONZFILE)
24
+ end
25
+ end
26
+
27
+ def initialize(options = {})
28
+ @loaded = false
29
+ self.merge! options
30
+ run_fonzfile
31
+ end
32
+
33
+ def operator
34
+ Operator.new(self)
35
+ end
36
+
37
+
38
+ def set_attr(hash)
39
+ config.merge! hash
40
+ end
41
+
42
+ def config
43
+ self
44
+ end
45
+
46
+ def config_loaded?
47
+ loaded
48
+ end
49
+
50
+ private
51
+
52
+ def run_fonzfile
53
+ if fonzfile?
54
+ eval fonzfile.read
55
+ @loaded = true
56
+ else
57
+ raise NoFonzfileError, "No Fonzfile has been found"
58
+ end
59
+ end
60
+
61
+ def fonzfile?
62
+ File.exists? config[:file_name] || FONZFILE
63
+ end
64
+
65
+ def fonzfile
66
+ File.open config[:file_name] || FONZFILE
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,28 @@
1
+ module Alphonse
2
+ module Configs
3
+ module Bundler
4
+ def bundle_exec(command)
5
+ use_bundle = using_bundler? ? "#{ruby_bin_path}bundle exec " : ""
6
+ "#{use_bundle}#{command}"
7
+ end
8
+
9
+ def bundle_install
10
+ "#{ruby_bin_path}bundle install --deployment --without development test" if using_bundler?
11
+ end
12
+
13
+ def rake(command)
14
+ bundle_exec "rake #{command}"
15
+ end
16
+
17
+ private
18
+
19
+ def using_bundler?
20
+ File.exists? "Gemfile"
21
+ end
22
+
23
+ def ruby_bin_path
24
+ config[:ruby_bin_path] rescue nil
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,90 @@
1
+ module Alphonse
2
+ module Configs
3
+ module Operation
4
+
5
+ attr_reader :command, :description
6
+
7
+ def operation(name, description, &block)
8
+ @command = name
9
+ set_attr "#{command}_description".to_sym => description
10
+ block.call if block_given?
11
+ end
12
+
13
+ def tasks(*args)
14
+ set_attr command.to_sym => args.flatten
15
+ end
16
+
17
+ end
18
+
19
+ module Tasks
20
+ # For testing only
21
+ def list
22
+ "ls -a"
23
+ end
24
+ alias :ls :list
25
+
26
+ def branch
27
+ config[:branch] || 'master'
28
+ end
29
+
30
+ def repository
31
+ config[:git_repo]
32
+ end
33
+
34
+ def set_path_variable
35
+ ["export PATH=#{config[:env_path]}"]
36
+ end
37
+
38
+ def source_shell_rc
39
+ "source ~/.bashrc"
40
+ end
41
+
42
+ def start_app
43
+ (config[:start_command] || 'touch tmp/restart.txt')
44
+ end
45
+
46
+ def restart_app
47
+ (config[:restart_command] || 'touch tmp/restart.txt')
48
+ end
49
+
50
+ def mkdir_path
51
+ "mkdir -p #{config[:path]}"
52
+ end
53
+ alias :setup_directory :mkdir_path
54
+
55
+ def cd_to_path
56
+ "cd #{config[:path]}"
57
+ end
58
+ alias :cd :cd_to_path
59
+
60
+ def git_pull
61
+ ["git checkout #{branch} -q", "git pull origin #{branch} -q", "git gc --aggressive"]
62
+ end
63
+ alias :gl :git_pull
64
+ alias :update_repository :git_pull
65
+
66
+ def git_clone
67
+ "git clone #{repository} #{config[:path]}"
68
+ end
69
+ alias :gc :git_clone
70
+ alias :clone_repository :git_clone
71
+
72
+ def bundle
73
+ bundle_install
74
+ end
75
+ alias :install_gems :bundle
76
+
77
+ def db_setup
78
+ rake("db:setup RAILS_ENV=#{config[:environment]}")
79
+ end
80
+ alias :setup_database :db_setup
81
+
82
+ def db_migrate
83
+ rake("db:migrate RAILS_ENV=#{config[:environment]}")
84
+ end
85
+ alias :update_database :db_migrate
86
+
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,48 @@
1
+ module Alphonse
2
+ module Configs
3
+ module Setting
4
+
5
+ # TODO: evaluate attributes so allow attribute reuse
6
+ # E.g.
7
+ # user = "user_name"
8
+ # path = "/full/path/to/#{user}" => "/full/path/to/user_name"
9
+
10
+ def user(value)
11
+ set_attr :user => value
12
+ end
13
+
14
+ def hosts(*value)
15
+ set_attr :hosts => [*value].flatten
16
+ end
17
+
18
+ def path(value)
19
+ set_attr :path => value
20
+ end
21
+
22
+ def git_repo(value)
23
+ set_attr :git_repo => value
24
+ end
25
+
26
+ def branch(value)
27
+ set_attr :branch => value
28
+ end
29
+
30
+ def env_path(value)
31
+ set_attr :env_path => value
32
+ end
33
+
34
+ def ruby_bin_path(value)
35
+ set_attr :ruby_bin_path => value
36
+ end
37
+
38
+ def start_command(value)
39
+ set_attr :start_command => value
40
+ end
41
+
42
+ def restart_command(value)
43
+ set_attr :restart_command => value
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+
3
+ require "net/ssh/session"
4
+
5
+ module Alphonse
6
+ class Connection
7
+
8
+ attr_reader :host, :user, :session, :config
9
+ def initialize(host, user, config)
10
+ @config = config
11
+ @host = host
12
+ @user = user
13
+
14
+ raise Alphonse::MissingUserError, "User has not been specified" unless user
15
+ raise Alphonse::MissingHostError, "Host has not been specified" unless host
16
+
17
+ @session = Net::SSH::Session.new(host, user)
18
+ end
19
+
20
+ def connection_string
21
+ "#{user}@#{host}"
22
+ end
23
+
24
+ def send_and_execute(command = "")
25
+ commands = parse(command)
26
+
27
+ session.open(10)
28
+
29
+ results = []
30
+ commands.each do |cmd|
31
+ Alphonse.logger.task cmd
32
+ result = session.run(cmd)
33
+ results << result
34
+ if result.failure?
35
+ Alphonse.logger.error result.output
36
+ break
37
+ else
38
+ Alphonse.logger.debug result.output if config[:verbose]
39
+ Alphonse.logger.info "Completed in #{result.duration}s"
40
+ end
41
+
42
+ end
43
+ results
44
+ end
45
+
46
+
47
+ def close
48
+ session.close
49
+ end
50
+
51
+ private
52
+
53
+ def parse(command)
54
+ case command.class.name
55
+ when 'String'
56
+ command == '' ? [] : [command.chomp]
57
+ when 'Array'
58
+ command.compact.flatten
59
+ else
60
+ raise Alphonse::MissingCommandError, "Command Not a String or Array: #{command.inspect}"
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,33 @@
1
+ # Settings
2
+ user 'remote_user'
3
+ hosts 'stage.example.com'
4
+ path "/fullpath/to/folder"
5
+ git_repo 'git@gitaddress:repository.git'
6
+ branch 'master'
7
+ ruby_bin_path '/path/to/ruby/'
8
+
9
+ operation :setup, 'Setup server' do
10
+ tasks :setup_directory
11
+ end
12
+
13
+ operation :deploy, 'Deploy repository' do
14
+ tasks :clone_repository, :install_gems, :setup_database, :restart_app
15
+ end
16
+
17
+ operation :update, 'Update the repository on the server' do
18
+ tasks :update_repository, :install_gems, :update_database, :restart_app
19
+ end
20
+
21
+ operation :restart, 'Restart application' do
22
+ tasks :restart_app
23
+ end
24
+
25
+
26
+ # Tasks
27
+ # setup_directory
28
+ # clone_repository
29
+ # update_repository
30
+ # install_gems
31
+ # setup_database
32
+ # update_database
33
+ # restart_app
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ require 'logger'
4
+
5
+ module Alphonse
6
+
7
+ class Logger < ::Logger
8
+ TASK = 6
9
+ SUCCESS = 7
10
+ OPERATION = 8
11
+
12
+ LABELS = %w(DEBUG INFO WARN ERROR FATAL ANY TASK SUCCESS OPERATION)
13
+
14
+ def task(progname = nil, &block)
15
+ add(TASK, nil, progname, &block)
16
+ end
17
+
18
+ def success(progname = nil, &block)
19
+ add(SUCCESS, nil, progname, &block)
20
+ end
21
+
22
+ def operation(progname = nil, &block)
23
+ add(OPERATION, nil, progname, &block)
24
+ end
25
+
26
+ private
27
+
28
+ def format_severity(severity)
29
+ LABELS[severity] || 'ANY'
30
+ end
31
+
32
+ end
33
+
34
+
35
+ class LogFormat
36
+
37
+ COLOURS = {
38
+ :black => 30,
39
+ :red => 31,
40
+ :green => 32,
41
+ :yellow => 33,
42
+ :blue => 34,
43
+ :magenta => 35,
44
+ :cyan => 36,
45
+ :white => 37
46
+ }
47
+
48
+ def call(severity, datetime, progname, message)
49
+
50
+ case severity
51
+ when "DEBUG"
52
+ colour("#{progname}::#{severity} - #{message}\n", :yellow)
53
+ when "WARN"
54
+ colour("#{progname}::#{severity} - #{message}\n", :yellow)
55
+ when "TASK"
56
+ colour("#{progname}::#{severity} - #{underline(message)}\n", :magenta)
57
+ when "SUCCESS"
58
+ colour("#{progname}::#{severity} - #{underline(message)}\n", :green)
59
+ when "OPERATION"
60
+ colour("#{progname}::#{severity} - #{underline(message)}\n", :cyan)
61
+ when "ERROR", "FATAL"
62
+ colour("#{progname}::#{severity} - #{underline(message)}\n", :red)
63
+ else
64
+ "#{progname}::#{severity} - #{message}\n\n"
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def colour(msg, clr = :black)
71
+ "\e[#{COLOURS[clr]}m#{msg}\e[0m"
72
+ end
73
+
74
+ def underline(message)
75
+ "\e[4m#{message}\e[0m"
76
+ end
77
+
78
+ end
79
+
80
+ def self.logger
81
+ @logger ||= begin
82
+ logger = Logger.new($stdout)
83
+ logger.level = $alphonse_log_level || ::Logger::INFO
84
+ logger.progname = 'Alphonse'
85
+ logger.formatter = LogFormat.new
86
+ logger
87
+ end
88
+ end
89
+
90
+
91
+ end