billy_the_tool 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 4pcbr
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Billy the tool
2
+
3
+ ## Commands
4
+
5
+ ### Setup existing project
6
+ * billy hello
7
+ * billy eat {billy_config}
8
+
9
+ ### Deploy existing project
10
+ * billy walk {app_name}
11
+
12
+ ## Sample config file
13
+
14
+ ```
15
+ deploy_to: /var/web/
16
+ user: user
17
+ server: 192.168.216.93
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```bash
23
+ cd ~
24
+ billy eat http://192.168.216.93/billy.cfg
25
+ cd project_name
26
+ billy walk project_name
27
+ open http://192.168.216.93/project_name/index.html
28
+ ```
data/bin/billy ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'billy'
6
+
7
+ Billy::Session.run!( ARGV )
@@ -0,0 +1,36 @@
1
+ require 'billy/commands'
2
+
3
+ class Billy
4
+ class Commands
5
+ class Command
6
+
7
+ def name
8
+ self.class.to_s.split( "::" ).last.downcase
9
+ end
10
+
11
+ protected
12
+
13
+ def initialize
14
+ end
15
+
16
+ def get_confirmation
17
+ gets.chomp.downcase == "y"
18
+ end
19
+
20
+ class << self
21
+
22
+ attr_accessor :_instance
23
+
24
+ def instance
25
+ self._instance ||= self.new
26
+ end
27
+
28
+ def register_self!
29
+ Billy::Commands.register_command!( instance )
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ require 'billy/commands/command'
2
+
3
+ class Billy
4
+ class Commands
5
+ class Config < Command
6
+
7
+ def proceed!( arguments = nil )
8
+ dir = Dir.pwd
9
+ if !Billy::Config.config_exists?( dir )
10
+ print "Billy config file could not be found here. Say Billy hello."
11
+ exit 1
12
+ else
13
+ Billy::Config.load!( dir )
14
+ print "Current Billy settings: \n"
15
+ Billy::Config.settings.each_pair do |k, v|
16
+ print "#{k}:\t\t\t#{v}\n"
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+
25
+ Billy::Commands::Config.register_self!
@@ -0,0 +1,68 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'billy/commands/command'
4
+
5
+ class Billy
6
+ class Commands
7
+ class Eat < Command
8
+
9
+ def proceed!( arguments )
10
+ ( path = arguments.shift ) unless arguments.nil?
11
+ begin
12
+ if path.nil? || path.empty?
13
+ raise "No config path given. \nYou have to provide some settings path, e.g. in your local filesystem or direct link to blob file in repository etc.\n"
14
+ elsif uri?( path ) && ( result = load_remote_config( path ) ).nil?
15
+ raise "Remote config file could not be loaded"
16
+ elsif file?( path ) && ( !File.exists?( path ) || ( result = File.read( path ) ).nil? )
17
+ raise "Config File not found: #{path}"
18
+ end
19
+ rescue Exception => e
20
+ print e.message
21
+ exit 1
22
+ end
23
+ eat_config( result )
24
+ save_config
25
+ end
26
+
27
+ def eat_config( config_string )
28
+ Billy::Config.instance.eat_string_config( config_string )
29
+ end
30
+
31
+ def save_config
32
+ print "Parsed config data:\n"
33
+ Billy::Config.settings.each_pair do |k, v|
34
+ print "#{k}: #{v}\n"
35
+ end
36
+ print "Save this settings?(y/n): "
37
+ confirm = get_confirmation
38
+ if !confirm
39
+ print "Billy didn't save config file. Skipping\n"
40
+ exit 1
41
+ end
42
+ Billy::Config.save
43
+ print "Billy saved config file to #{Billy::Config::BILLYRC}\n"
44
+ end
45
+
46
+ def uri?( str )
47
+ begin
48
+ uri = URI.parse( str )
49
+ %w( http https ).include?( uri.scheme )
50
+ rescue
51
+ false
52
+ end
53
+ end
54
+
55
+ def file?( str )
56
+ !uri?( str )
57
+ end
58
+
59
+ def load_remote_config( path )
60
+ uri = URI( path )
61
+ Net::HTTP.get( uri )
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+
68
+ Billy::Commands::Eat.register_self!
@@ -0,0 +1,79 @@
1
+ require 'billy/commands/command'
2
+
3
+ class Billy
4
+ class Commands
5
+ class Hello < Command
6
+
7
+ def proceed!( arguments = nil )
8
+ billy_say_hello
9
+ path = get_init_path( arguments )
10
+ config = Billy::Config.instance
11
+ config.save!( path, true )
12
+ if !ssh_command_exists?
13
+ suggest_install_ssh
14
+ exit 1
15
+ end
16
+ offer_ssh_keygen unless ssh_key_exists?
17
+ print "All done!\n"
18
+ end
19
+
20
+ def billy_say_hello
21
+ print "Hi! I'm Billy, simple deploy tool.\n"
22
+ print "Usage:\n"
23
+ print " * billy hello (path) -- init billy in {path} folder. Inites in current if no one given.\n"
24
+ print " * billy eat {cfg_path} -- parse and save billy config in current folder. {cfg_path} here means remote file url or local one.\n"
25
+ print " * billy walk {application_name} -- deploy HEAD version in repository to remote server.\n"
26
+ end
27
+
28
+ def ssh_command_exists?
29
+ res = true
30
+ %w(ssh ssh-keygen).each do |cmd|
31
+ res &= system( "which #{cmd} 2>&1 > /dev/null" )
32
+ end
33
+ res
34
+ end
35
+
36
+ def ssh_key_exists?
37
+ ssh_root_path = File.expand_path( "~/.ssh" )
38
+ res = true
39
+ res &= File.exists?( ssh_root_path )
40
+ res &= File.directory?( ssh_root_path )
41
+ res &= Dir[ ssh_root_path + "/*.pub" ].any?
42
+ res
43
+ end
44
+
45
+ def suggest_install_ssh
46
+ print "Billy wants you to install ssh command. Please do it first.\n"
47
+ end
48
+
49
+ def offer_ssh_keygen
50
+ print "Billy did not find your ssh key. Would you like to create it now?(y/n): "
51
+ confirm = get_confirmation
52
+ if !confirm
53
+ print "Ssh key should be generated before we continue. Please generate it.\n"
54
+ exit 1
55
+ end
56
+ enc_type = 'rsa'
57
+ print "Billy creates ssh keys for you...\n"
58
+ system "ssh-keygen -t #{enc_type} -N '' -f ~/.ssh/id_#{enc_type}"
59
+ print "All done!"
60
+ end
61
+
62
+ def get_init_path( arguments )
63
+ ( path = arguments.shift ) unless arguments.nil?
64
+ if path.nil? || path.empty?
65
+ print "Billy will be inited in current directory. Proceed?(y/n): "
66
+ confirm = get_confirmation
67
+ if !confirm
68
+ print "Billy has nothing to do. Bye-bye."
69
+ end
70
+ path = Dir.pwd
71
+ end
72
+ File.expand_path( path )
73
+ end
74
+
75
+ end
76
+ end
77
+ end
78
+
79
+ Billy::Commands::Hello.register_self!
@@ -0,0 +1,130 @@
1
+ require 'billy/commands/command'
2
+ require 'capistrano'
3
+
4
+ class Billy
5
+ class Commands
6
+ class Walk < Command
7
+
8
+ GIT_PATH = ".git"
9
+
10
+ def proceed!( arguments = nil )
11
+
12
+ if !Billy::Config.load
13
+ print "Billy config could not be found. Say Billy hello."
14
+ exit 1
15
+ end
16
+
17
+ destination = ( arguments.shift rescue nil ) || Billy::Config.instance.destination
18
+
19
+ if destination.nil?
20
+ print "Billy doesn't know where to walk."
21
+ print "You have to provide deploy folder, e.g.: billy walk my_super_project.\n"
22
+ exit 1
23
+ end
24
+
25
+ cap = prepare_capistrano( destination )
26
+ %w(deploy:setup deploy).each do |command|
27
+ cap.find_and_execute_task(command, :before => :start, :after => :finish)
28
+ end
29
+ cap.trigger( :exit )
30
+
31
+ print "All done! Billy is a clever boy!\n"
32
+ end
33
+
34
+ def prepare_capistrano( destination )
35
+
36
+ cap = Capistrano::Configuration.new
37
+ cap.load "standard"
38
+ cap.load "deploy"
39
+ cap.trigger( :load )
40
+ config = Billy::Config.instance
41
+
42
+ if config.deploy_to.nil?
43
+ print "Billy doesn't know remote deploy path. Please provide it in config under deploy_to key.\n"
44
+ exit 1
45
+ end
46
+
47
+ cap.set :scm, "git"
48
+ cap.set :use_sudo, false
49
+ cap.set :deploy_via, :remote_cache
50
+ cap.set :application, destination
51
+ cap.set :deploy_to, File.join( config.deploy_to, destination )
52
+ cap.server config.server, :app, :web, :db, :primary => true
53
+ cap.set :user, config.user
54
+
55
+ repository = config.repository || get_repository_path
56
+
57
+ cap.set :repository, repository
58
+ cap.set :normalize_asset_timestamps, false
59
+
60
+ cap.namespace :deploy do
61
+ cap.task :start, :roles => :app do; end
62
+ cap.task :stop, :roles => :app do; end
63
+ cap.task :restart, :roles => :app do; end
64
+ end
65
+
66
+ cap
67
+ end
68
+
69
+ def get_repository_path
70
+ if !local_repository_exists?
71
+ print "Git repository was not created. Do you want Billy to create it now?\n"
72
+ confirm = get_confirmation
73
+ if !confirm
74
+ print "Billy could not proceed without git repository.\n"
75
+ exit 1
76
+ else
77
+ print "Creating git repository.\n"
78
+ init_git_repository
79
+ end
80
+ end
81
+
82
+ config = get_git_config
83
+
84
+ if !remote_repository_exists?( config )
85
+ print "Billy could not find remote repository for your project.\n"
86
+ print "Please add some remote, e.g. git remote add git@github.com:myUsername/myProject.git.\n"
87
+ exit 1
88
+ end
89
+
90
+ idx = 1
91
+ i = 0
92
+
93
+ match_data = get_remotes( config )
94
+
95
+ if match_data.length > 1
96
+ print "Billy found several remotes in repository. Choose one to deploy from:"
97
+ match_data.each do |remote|
98
+ i += 1
99
+ print "#{i}: #{remote[0]}\t\t#{remote[1]}\n"
100
+ end
101
+ while ( idx = gets.chomp.to_i ) > match_data.length; end
102
+ end
103
+ match_data[ idx - 1 ][ 1 ]
104
+ end
105
+
106
+ def get_git_config
107
+ File.read( File.expand_path( GIT_PATH + "/config" ) )
108
+ end
109
+
110
+ def local_repository_exists?
111
+ File.exists?( File.expand_path( GIT_PATH ) )
112
+ end
113
+
114
+ def remote_repository_exists?( config )
115
+ get_remotes( config ).any?
116
+ end
117
+
118
+ def get_remotes( config )
119
+ config.gsub( /\n/, ';' ).scan /\[remote\s+\"([^"]+)\"\][^\[]+url\s*=\s*([^;]+)/
120
+ end
121
+
122
+ def init_git_repository
123
+ system "git init ."
124
+ end
125
+
126
+ end
127
+ end
128
+ end
129
+
130
+ Billy::Commands::Walk.register_self!
@@ -0,0 +1,24 @@
1
+ class Billy
2
+ class Commands
3
+
4
+ class << self
5
+
6
+ attr_accessor :pool
7
+
8
+ def load_pool!
9
+ self.pool ||= Hash.new
10
+ commands_path = File.expand_path( File.dirname(__FILE__) + '/commands/**/*.rb' )
11
+ Dir[ commands_path ].each do |file|
12
+ require file
13
+ end
14
+ end
15
+
16
+ def register_command!( command )
17
+ load_pool! unless !pool.nil?
18
+ ( pool[ command.name ] = command ) unless pool.values.include?( command )
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,117 @@
1
+ class Billy
2
+ class Config
3
+
4
+ BILLYRC = '.billyrc'
5
+ SEPARATOR = ': '
6
+
7
+ attr_accessor :storage
8
+ attr_accessor :storage_path
9
+
10
+ def method_missing( m, *args, &block )
11
+ if m.to_s[ /=$/ ].nil?
12
+ self.storage[ m.to_s ]
13
+ else
14
+ key = ( m.to_s )[ /^([^=]+)/ ]
15
+ val = args.shift
16
+ ( self.storage[ key ] = val ) unless key.nil? && key.empty?
17
+ end
18
+ end
19
+
20
+ def config_exists?( dir )
21
+ File.exists?( File.expand_path( dir + "/#{BILLYRC}" ) )
22
+ end
23
+
24
+ def to_s
25
+ [].tap { |res|
26
+ self.storage.each_pair do |k, v|
27
+ res.push "#{k}#{SEPARATOR}#{v}"
28
+ end
29
+ }.push( "" ).join( "\n" )
30
+ end
31
+
32
+ def load
33
+ %w(. ~).each do |path|
34
+ begin
35
+ load!( File.expand_path( path ) )
36
+ return true
37
+ rescue
38
+ next
39
+ end
40
+ end
41
+
42
+ false
43
+ end
44
+
45
+ def load!( dir )
46
+ self.storage_path = File.expand_path( dir )
47
+ file_path = storage_path + "/#{BILLYRC}"
48
+ raise "Config was not found in #{path}" unless File.exists?( file_path )
49
+ eat_string_config( File.read( file_path ) )
50
+ end
51
+
52
+ def clear
53
+ self.storage.clear
54
+ end
55
+
56
+ def reload!( dir = nil )
57
+ dir ||= storage_path
58
+ clear
59
+ load!( dir )
60
+ end
61
+
62
+ def save( dir = nil )
63
+ dir ||= Dir.pwd
64
+ begin
65
+ save!( dir, true )
66
+ true
67
+ rescue
68
+ false
69
+ end
70
+ end
71
+
72
+ def save!( dir, force = false )
73
+ raise 'Directory name should not be empty' unless !dir.empty?
74
+ raise "Directory #{dir.to_s} doesn't exist" unless File.exist?( File.expand_path( dir ) )
75
+ billyrc_path = File.expand_path( dir + "/#{BILLYRC}" )
76
+ raise "Config already exists in #{billyrc_path}" unless !File.exists?( billyrc_path ) || force
77
+ File.open( billyrc_path, 'w' ) { |file|
78
+ file.flush
79
+ file.write( self.to_s )
80
+ }
81
+ end
82
+
83
+ def eat_string_config( string_config )
84
+ clear
85
+ string_config.each_line do |line|
86
+ next unless !line.empty?
87
+ items = line.split( SEPARATOR )
88
+ k = items.shift
89
+ v = items.join( SEPARATOR ).strip
90
+ ( self.storage[ k.to_s ] = v ) unless k.nil? || k.empty? || v.nil? || v.empty?
91
+ end
92
+ end
93
+
94
+ protected
95
+
96
+ def initialize
97
+ self.storage = Hash.new
98
+ end
99
+
100
+ class << self
101
+
102
+ def instance
103
+ @@instance ||= self.new
104
+ end
105
+
106
+ def settings
107
+ instance.storage
108
+ end
109
+
110
+ def method_missing( m, *args, &block )
111
+ instance.send( m, *args, &block ) if instance.respond_to?( m )
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,29 @@
1
+ class Billy
2
+ class Session
3
+ class << self
4
+
5
+ def run!( args )
6
+ Billy::Commands.load_pool!
7
+ command = ARGV.shift
8
+ arguments = ARGV
9
+ if command.nil?
10
+ print "Billy has nothing to do. Yay!\n"
11
+ exit 0
12
+ end
13
+ status = proceed_command!( command, arguments )
14
+ exit status
15
+ end
16
+
17
+ def proceed_command!( command_name, arguments )
18
+ cmd = Billy::Commands.pool[ command_name.to_s ]
19
+ if cmd.nil?
20
+ print "Billy doesn't know this command: #{command_name}. Say billy hello.\n"
21
+ return 1
22
+ end
23
+ cmd.proceed!( arguments )
24
+ 0
25
+ end
26
+
27
+ end
28
+ end
29
+ end
data/lib/billy.rb ADDED
@@ -0,0 +1,7 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ class Billy
4
+ require 'billy/session'
5
+ require 'billy/commands'
6
+ require 'billy/config'
7
+ end
metadata ADDED
@@ -0,0 +1,199 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: billy_the_tool
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 2
10
+ version: 0.1.2
11
+ platform: ruby
12
+ authors:
13
+ - 4pcbr
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-11-29 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ hash: 49
28
+ segments:
29
+ - 2
30
+ - 13
31
+ - 5
32
+ version: 2.13.5
33
+ type: :runtime
34
+ name: capistrano
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ name: shoulda
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ hash: 31
58
+ segments:
59
+ - 3
60
+ - 12
61
+ version: "3.12"
62
+ type: :development
63
+ name: rdoc
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ hash: 27
73
+ segments:
74
+ - 1
75
+ - 2
76
+ - 2
77
+ version: 1.2.2
78
+ type: :development
79
+ name: bundler
80
+ version_requirements: *id004
81
+ - !ruby/object:Gem::Dependency
82
+ prerelease: false
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ hash: 63
89
+ segments:
90
+ - 1
91
+ - 8
92
+ - 4
93
+ version: 1.8.4
94
+ type: :development
95
+ name: jeweler
96
+ version_requirements: *id005
97
+ - !ruby/object:Gem::Dependency
98
+ prerelease: false
99
+ requirement: &id006 !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ type: :development
109
+ name: rcov
110
+ version_requirements: *id006
111
+ - !ruby/object:Gem::Dependency
112
+ prerelease: false
113
+ requirement: &id007 !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ~>
117
+ - !ruby/object:Gem::Version
118
+ hash: 63
119
+ segments:
120
+ - 2
121
+ - 12
122
+ - 0
123
+ version: 2.12.0
124
+ type: :development
125
+ name: rspec
126
+ version_requirements: *id007
127
+ - !ruby/object:Gem::Dependency
128
+ prerelease: false
129
+ requirement: &id008 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ~>
133
+ - !ruby/object:Gem::Version
134
+ hash: 49
135
+ segments:
136
+ - 2
137
+ - 13
138
+ - 5
139
+ version: 2.13.5
140
+ type: :runtime
141
+ name: capistrano
142
+ version_requirements: *id008
143
+ description: Billy is simplified deploy system based on top of capistrano
144
+ email: me@4pcbr.com
145
+ executables:
146
+ - billy
147
+ extensions: []
148
+
149
+ extra_rdoc_files:
150
+ - LICENSE.txt
151
+ - README.md
152
+ files:
153
+ - bin/billy
154
+ - lib/billy.rb
155
+ - lib/billy/commands.rb
156
+ - lib/billy/commands/command.rb
157
+ - lib/billy/commands/config.rb
158
+ - lib/billy/commands/eat.rb
159
+ - lib/billy/commands/hello.rb
160
+ - lib/billy/commands/walk.rb
161
+ - lib/billy/config.rb
162
+ - lib/billy/session.rb
163
+ - LICENSE.txt
164
+ - README.md
165
+ homepage: http://github.com/AltSpace/billy
166
+ licenses:
167
+ - MIT
168
+ post_install_message:
169
+ rdoc_options: []
170
+
171
+ require_paths:
172
+ - lib
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ none: false
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ hash: 3
179
+ segments:
180
+ - 0
181
+ version: "0"
182
+ required_rubygems_version: !ruby/object:Gem::Requirement
183
+ none: false
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ hash: 3
188
+ segments:
189
+ - 0
190
+ version: "0"
191
+ requirements: []
192
+
193
+ rubyforge_project:
194
+ rubygems_version: 1.8.24
195
+ signing_key:
196
+ specification_version: 3
197
+ summary: Billy the tool
198
+ test_files: []
199
+