webbynode 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of webbynode might be problematic. Click here for more details.

Files changed (98) hide show
  1. data/Manifest +88 -4
  2. data/PostInstall.txt +42 -9
  3. data/README.rdoc +6 -0
  4. data/Rakefile +16 -19
  5. data/assets/webbynode.png +0 -0
  6. data/bin/webbynode +3 -3
  7. data/bin/wn +7 -0
  8. data/cucumber.yml +1 -0
  9. data/devver.rake +174 -0
  10. data/features/bootstrap.feature +17 -0
  11. data/features/step_definitions/command_steps.rb +14 -0
  12. data/features/support/env.rb +22 -0
  13. data/features/support/hooks.rb +8 -0
  14. data/{test/test_webbynode.rb → features/support/io_features.rb} +0 -0
  15. data/features/support/mocha.rb +15 -0
  16. data/lib/templates/api_token +14 -0
  17. data/lib/templates/backup +15 -0
  18. data/lib/templates/gitignore +4 -0
  19. data/lib/templates/help +53 -0
  20. data/lib/webbynode/api_client.rb +121 -0
  21. data/lib/webbynode/application.rb +21 -0
  22. data/lib/webbynode/command.rb +332 -0
  23. data/lib/webbynode/commands/add_backup.rb +33 -0
  24. data/lib/webbynode/commands/add_key.rb +24 -0
  25. data/lib/webbynode/commands/alias.rb +153 -0
  26. data/lib/webbynode/commands/change_dns.rb +29 -0
  27. data/lib/webbynode/commands/config.rb +15 -0
  28. data/lib/webbynode/commands/delete.rb +25 -0
  29. data/lib/webbynode/commands/help.rb +25 -0
  30. data/lib/webbynode/commands/init.rb +77 -0
  31. data/lib/webbynode/commands/push.rb +70 -0
  32. data/lib/webbynode/commands/remote.rb +28 -0
  33. data/lib/webbynode/commands/restart.rb +25 -0
  34. data/lib/webbynode/commands/start.rb +23 -0
  35. data/lib/webbynode/commands/stop.rb +23 -0
  36. data/lib/webbynode/commands/tasks.rb +149 -0
  37. data/lib/webbynode/commands/version.rb +8 -0
  38. data/lib/webbynode/commands/webbies.rb +30 -0
  39. data/lib/webbynode/git.rb +112 -0
  40. data/lib/webbynode/io.rb +175 -0
  41. data/lib/webbynode/notify.rb +19 -0
  42. data/lib/webbynode/option.rb +84 -0
  43. data/lib/webbynode/parameter.rb +27 -0
  44. data/lib/webbynode/properties.rb +43 -0
  45. data/lib/webbynode/push_and.rb +16 -0
  46. data/lib/webbynode/remote_executor.rb +21 -0
  47. data/lib/webbynode/server.rb +39 -0
  48. data/lib/webbynode/ssh.rb +65 -0
  49. data/lib/webbynode/ssh_keys.rb +36 -0
  50. data/lib/webbynode.rb +56 -0
  51. data/spec/fixtures/aliases +0 -0
  52. data/spec/fixtures/api/credentials +3 -0
  53. data/spec/fixtures/api/dns +30 -0
  54. data/spec/fixtures/api/dns_a_record +20 -0
  55. data/spec/fixtures/api/dns_a_record_already_exists +15 -0
  56. data/spec/fixtures/api/dns_a_record_error +13 -0
  57. data/spec/fixtures/api/dns_new_zone +17 -0
  58. data/spec/fixtures/api/webbies +26 -0
  59. data/spec/fixtures/api/webbies_unauthorized +12 -0
  60. data/spec/fixtures/api/webby +6 -0
  61. data/spec/fixtures/commands/tasks/after_push +3 -0
  62. data/spec/fixtures/fixture_helpers +2 -0
  63. data/spec/fixtures/git/config/210.11.13.12 +9 -0
  64. data/spec/fixtures/git/config/67.23.79.31 +9 -0
  65. data/spec/fixtures/git/config/67.23.79.32 +9 -0
  66. data/spec/fixtures/git/config/config +9 -0
  67. data/spec/fixtures/git/status/clean +2 -0
  68. data/spec/fixtures/git/status/dirty +9 -0
  69. data/spec/fixtures/pushand +2 -0
  70. data/spec/spec_helper.rb +58 -0
  71. data/spec/webbynode/api_client_spec.rb +227 -0
  72. data/spec/webbynode/application_spec.rb +50 -0
  73. data/spec/webbynode/command_spec.rb +285 -0
  74. data/spec/webbynode/commands/add_backup_spec.rb +66 -0
  75. data/spec/webbynode/commands/add_key_spec.rb +58 -0
  76. data/spec/webbynode/commands/alias_spec.rb +213 -0
  77. data/spec/webbynode/commands/change_dns_spec.rb +51 -0
  78. data/spec/webbynode/commands/config_spec.rb +22 -0
  79. data/spec/webbynode/commands/delete_spec.rb +78 -0
  80. data/spec/webbynode/commands/help_spec.rb +43 -0
  81. data/spec/webbynode/commands/init_spec.rb +326 -0
  82. data/spec/webbynode/commands/push_spec.rb +175 -0
  83. data/spec/webbynode/commands/remote_spec.rb +76 -0
  84. data/spec/webbynode/commands/tasks_spec.rb +288 -0
  85. data/spec/webbynode/commands/version_spec.rb +18 -0
  86. data/spec/webbynode/commands/webbies_spec.rb +27 -0
  87. data/spec/webbynode/git_spec.rb +310 -0
  88. data/spec/webbynode/io_spec.rb +198 -0
  89. data/spec/webbynode/option_spec.rb +48 -0
  90. data/spec/webbynode/parameter_spec.rb +70 -0
  91. data/spec/webbynode/push_and_spec.rb +28 -0
  92. data/spec/webbynode/remote_executor_spec.rb +25 -0
  93. data/spec/webbynode/server_spec.rb +101 -0
  94. data/webbynode.gemspec +25 -16
  95. metadata +182 -14
  96. data/lib/wn.rb +0 -106
  97. data/test/test_helper.rb +0 -9
  98. data/test/test_wn.rb +0 -220
@@ -0,0 +1,29 @@
1
+ module Webbynode::Commands
2
+ class ChangeDns < Webbynode::Command
3
+ requires_initialization!
4
+
5
+ summary "Changes the DNS entry for this application"
6
+ parameter :dns_entry, "New DNS entry for this application"
7
+
8
+ add_alias "dns"
9
+
10
+ def execute
11
+ raise CommandError,
12
+ "Cannot change DNS because you have pending changes. Do a git commit or add changes to .gitignore." unless git.clean?
13
+
14
+ io.log "Changing DNS to #{param(:dns_entry)}...", :quiet_start
15
+
16
+ git.delete_file ".webbynode/config"
17
+ handle_dns param(:dns_entry)
18
+
19
+ app_name = io.app_name
20
+ io.create_file(".pushand", "#! /bin/bash\nphd $0 #{app_name} #{param(:dns_entry)}\n", true)
21
+
22
+ git.add ".pushand"
23
+ git.add ".webbynode/settings" if io.file_exists?(".webbynode/settings")
24
+ git.commit "Changed DNS to \"#{param(:dns_entry)}\""
25
+
26
+ io.log "Your application will start responding to #{param(:dns_entry)} after next deployment."
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ module Webbynode::Commands
2
+ class Config < Webbynode::Command
3
+ summary "Adds or changes your Webbynode API credentials"
4
+ option :email, "The email you use on Webbymanager"
5
+ option :token, "The API Token, found on Account section of Webbymanager"
6
+
7
+ def execute
8
+ value = {}
9
+ value[:email] = option(:email) if option(:email)
10
+ value[:token] = option(:token) if option(:token)
11
+
12
+ api.init_credentials(value)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module Webbynode::Commands
2
+ class Delete < Webbynode::Command
3
+ summary "Removes the current application from your Webby"
4
+ option :force, "Skips confirmation and forces the deletion of the app"
5
+
6
+ add_alias "rm"
7
+ add_alias "remove"
8
+
9
+ def execute
10
+ unless pushand.present?
11
+ io.log("Ahn!? Hello, McFly, anybody home?", true)
12
+ return
13
+ end
14
+
15
+ app_name = pushand.parse_remote_app_name
16
+ if option(:force) or ask("Do you really want to delete application #{app_name} (y/n)? ").downcase == "y"
17
+ notify("Removing [#{app_name}] from your webby...")
18
+ remote_executor.exec "delete_app #{app_name} --force", true
19
+ notify("The application [#{app_name}] has been removed from your webby.\n\nThe webserver is restarting.")
20
+ else
21
+ puts "Aborted."
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Webbynode::Commands
2
+ class Help < Webbynode::Command
3
+ summary "Guess what? You're on it!"
4
+ parameter :command, "Command to get help on"
5
+
6
+ def execute
7
+ if param(:command) == "commands"
8
+ puts "usage: webbynode COMMAND"
9
+ puts
10
+ puts "Available commands:"
11
+ dir = File.join(File.expand_path(File.dirname(__FILE__)), "/*.rb")
12
+ Dir[dir].each do |file|
13
+ command = file.split("/").last
14
+ command.gsub!(/\.rb/, "")
15
+
16
+ puts " #{command.ljust(10)} #{Webbynode::Command.class_for(command).setting(:summary)}"
17
+ end
18
+ puts
19
+ puts "Try 'webbynode help COMMAND' for more information."
20
+ else
21
+ puts Help.for(param(:command)).help
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,77 @@
1
+ module Webbynode::Commands
2
+ class Init < Webbynode::Command
3
+ summary "Prepares the application on current folder for deployment"
4
+ parameter :webby, String, "Name or IP of the Webby to deploy to"
5
+ parameter :dns, String, "The DNS used for this application", :required => false
6
+ option :adddns, "Creates the DNS entries for the domain"
7
+ option :engine, "Sets the application engine for the app", :validate => { :in => ['php', 'rack', 'rails', 'rails3'] }
8
+
9
+ def execute
10
+ unless params.any?
11
+ io.log help
12
+ return
13
+ end
14
+
15
+ webby = param(:webby)
16
+ app_name = io.app_name
17
+
18
+ if param(:dns)
19
+ dns_entry = "#{param(:dns)}"
20
+ else
21
+ dns_entry = app_name
22
+ end
23
+
24
+ io.log "Initializing application #{app_name} #{dns_entry ? "with dns #{dns_entry}" : ""}", :start
25
+
26
+ if webby =~ /\b(?:\d{1,3}\.){3}\d{1,3}\b/
27
+ webby_ip = webby
28
+ else
29
+ begin
30
+ io.log "Retrieving IP for Webby #{webby}...", :action
31
+ webby_ip = api.ip_for(webby)
32
+ unless webby_ip
33
+ if (webbies = api.webbies.keys) and webbies.any?
34
+ raise CommandError,
35
+ "Couldn't find Webby '#{webby}' on your account. Your Webbies are: #{webbies.map { |w| "'#{w}'"}.to_phrase}."
36
+ else
37
+ raise CommandError, "You don't have any active Webbies on your account."
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ io.log "Initializing directory structure...", :action
44
+ git.add_git_ignore unless io.file_exists?(".gitignore")
45
+
46
+ unless io.file_exists?(".pushand")
47
+ io.create_file(".pushand", "#! /bin/bash\nphd $0 #{app_name} #{dns_entry}\n", true)
48
+ end
49
+
50
+ unless io.directory?(".webbynode")
51
+ io.exec("mkdir -p .webbynode/tasks")
52
+ io.create_file(".webbynode/tasks/after_push", "")
53
+ io.create_file(".webbynode/tasks/before_push", "")
54
+ io.create_file(".webbynode/aliases", "")
55
+ io.create_file(".webbynode/config", "")
56
+ end
57
+
58
+ io.add_setting("engine", option(:engine)) if option(:engine)
59
+
60
+ unless git.present?
61
+ io.log "Initializing git and applying initial commit...", :action
62
+ git.init
63
+ git.add "."
64
+ git.commit "Initial commit"
65
+ end
66
+
67
+ io.log "Adding webbynode as git remote...", :action
68
+ git.add_remote "webbynode", webby_ip, app_name
69
+
70
+ handle_dns param(:dns) if option(:adddns)
71
+
72
+ io.log "Application #{app_name} ready for Rapid Deployment", :finish
73
+ rescue Webbynode::GitRemoteAlreadyExistsError
74
+ io.log "Application already initialized.", true
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,70 @@
1
+ module Webbynode::Commands
2
+ class Push < Webbynode::Command
3
+
4
+ requires_initialization!
5
+
6
+ attr_accessor :app_name, :before_tasks, :after_tasks
7
+
8
+ add_alias "deploy"
9
+
10
+ summary "Sends pending changes on the current application to your Webby"
11
+ option :dirty, "Allows pushing even if the current application has git changes pending"
12
+
13
+ def initialize(*args)
14
+ super
15
+ @before_tasks = Webbynode::Commands::Tasks.new
16
+ @after_tasks = Webbynode::Commands::Tasks.new
17
+ end
18
+
19
+ def execute
20
+ unless option(:dirty)
21
+ raise CommandError,
22
+ "Cannot push because you have pending changes. Do a git commit or add changes to .gitignore." unless git.clean?
23
+ end
24
+
25
+ @app_name = pushand.parse_remote_app_name
26
+
27
+ # Ensures there are Task Files to read from
28
+ before_tasks.ensure_tasks_folder
29
+
30
+ # Reads out the "before push" tasks file to see if there are any tasks that must be performed
31
+ # It will perform the "before push" tasks if there are any available
32
+ before_tasks.read_tasks(Webbynode::Commands::Tasks::BeforePushTasksFile)
33
+ perform_before_tasks if before_tasks.has_tasks?
34
+
35
+ # Logs a initialization message to the user
36
+ # Pushes the application to Webbynode
37
+ io.log "Pushing #{app_name}", :start
38
+ io.exec("git push webbynode master", false)
39
+
40
+ # Reads out the "after push" tasks file to see if there are any tasks that must be performed
41
+ # It will perform the "after push" tasks if there are any available
42
+ after_tasks.read_tasks(Webbynode::Commands::Tasks::AfterPushTasksFile)
43
+ perform_after_tasks if after_tasks.has_tasks?
44
+
45
+ io.log "Finished pushing #{app_name}", :finish
46
+ end
47
+
48
+
49
+ private
50
+
51
+ # Performs the before push tasks locally
52
+ def perform_before_tasks
53
+ io.log "Performing Before Push Tasks...", :action
54
+ before_tasks.session_tasks.each do |task|
55
+ io.log " Performing Task: #{task}", :action
56
+ io.exec(task)
57
+ end
58
+ end
59
+
60
+ # Performs the after push tasks remotely from the application root
61
+ def perform_after_tasks
62
+ io.log "Performing After Push Tasks...", :action
63
+ after_tasks.session_tasks.each do |task|
64
+ io.log " Performing Task: #{task}", :action
65
+ remote_executor.exec("cd #{pushand.parse_remote_app_name}; #{task}", true)
66
+ end
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,28 @@
1
+ module Webbynode::Commands
2
+ class NoOptionsProvided < StandardError; end
3
+
4
+ class Remote < Webbynode::Command
5
+ summary "Execute commands on your Webby for the current application"
6
+
7
+ requires_initialization!
8
+ requires_options!
9
+
10
+ parameter :command, Array, "Commands to execute"
11
+
12
+ def execute
13
+ unless server.application_pushed?
14
+ io.log "Before being able to run remote commands from your webby, you must first push your application to it."
15
+ exit
16
+ end
17
+
18
+ # Parses Pushand File and returns remote application name
19
+ remote_app_name = pushand.parse_remote_app_name
20
+
21
+ # Notify the user
22
+ io.log("Performing the requested remote command..", true)
23
+
24
+ # Executes the command on the remote server inside the application root folder
25
+ remote_executor.exec("cd #{remote_app_name}; #{param_values.join(" ")}", true)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ module Webbynode::Commands
2
+ class Restart < Webbynode::Command
3
+ summary "Reboots your Webby"
4
+ add_alias "reboot"
5
+
6
+ requires_initialization!
7
+
8
+ def execute
9
+ exit if no?("Are you sure you wish to restart your webby? (y/n)")
10
+
11
+ api.webbies.each do |webby|
12
+ if webby[1][:ip].eql?(git.parse_remote_ip)
13
+ if webby[1][:status].eql?("on")
14
+ puts "#{webby[0]} will now be rebooted!"
15
+ api.post("/webby/#{webby[1][:name]}/reboot")
16
+ else
17
+ puts "#{webby[0]} is starting up!"
18
+ api.post("/webby/#{webby[1][:name]}/start")
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module Webbynode::Commands
2
+ class Start < Webbynode::Command
3
+ summary "Starts your Webby, when it's off"
4
+ add_alias "boot"
5
+
6
+ requires_initialization!
7
+
8
+ def execute
9
+ exit if no?("Are you sure you wish to start your webby? (y/n)")
10
+
11
+ api.webbies.each do |webby|
12
+ if webby[1][:ip].eql?(git.parse_remote_ip)
13
+ unless webby[1][:status].eql?("on")
14
+ puts "#{webby[0]} is starting up!"
15
+ api.post("/webby/#{webby[1][:name]}/start")
16
+ else
17
+ puts "#{webby[0]} is already started up."
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Webbynode::Commands
2
+ class Stop < Webbynode::Command
3
+ summary "Shuts down your Webby"
4
+ add_alias "shutdown"
5
+
6
+ requires_initialization!
7
+
8
+ def execute
9
+ exit if no?("Are you sure you wish to shutdown your webby? (y/n)")
10
+
11
+ api.webbies.each do |webby|
12
+ if webby[1][:ip].eql?(git.parse_remote_ip)
13
+ unless webby[1][:status].eql?("off")
14
+ puts "#{webby[0]} will now shutdown!"
15
+ api.post("/webby/#{webby[1][:name]}/shutdown")
16
+ else
17
+ puts "#{webby[0]} is already shutdown."
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,149 @@
1
+ module Webbynode::Commands
2
+ class Tasks < Webbynode::Command
3
+ requires_initialization!
4
+
5
+ attr_accessor :action, :type, :command, :session_file, :session_tasks
6
+
7
+ summary "Manages tasks executed before or after you push your changes"
8
+ parameter :action, String, "add, remove or show.", :required => true, :validate => { :in => ["add", "remove", "show"] }
9
+ parameter :type, String, "before_push or after_push.", :required => true, :validate => { :in => ["before_push", "after_push"]}
10
+ parameter :command, Array, "Task to perform.", :required => false
11
+
12
+ # Constants
13
+ # Paths to the webbynode task files
14
+ TasksPath = ".webbynode/tasks"
15
+ BeforePushTasksFile = ".webbynode/tasks/before_push"
16
+ AfterPushTasksFile = ".webbynode/tasks/after_push"
17
+
18
+ def initialize(*args)
19
+ super
20
+ @session_tasks = Array.new
21
+ end
22
+
23
+ def execute
24
+
25
+ # Ensures that the tasks folder (.webbynode/tasks) is present
26
+ # Will create the task files if they are not present
27
+ ensure_tasks_folder
28
+
29
+ # Should parse the parameters
30
+ parse_parameters
31
+
32
+ # Sets the current path, extracted from @type
33
+ set_session_file
34
+
35
+ # Reads out the currently set tasks
36
+ read_tasks(session_file)
37
+
38
+ # Initializes either [add], [remove] or [show] depending on user input
39
+ send(action)
40
+
41
+ end
42
+
43
+ # Checks to see if the session_tasks has any tasks
44
+ def has_tasks?
45
+ return true unless session_tasks.empty?
46
+ false
47
+ end
48
+
49
+ # Reads the tasks straight from the specified file and stores them inside (in order)
50
+ # the session_tasks method.
51
+ def read_tasks(file)
52
+ @session_tasks = []
53
+ io.read_file(file).each_line do |line|
54
+ @session_tasks << line.gsub(/\n/,'') unless line.blank?
55
+ end
56
+ end
57
+
58
+ # Ensures the presence of the .webbynode/tasks folder
59
+ # Will create the necessary task files when they are not available
60
+ def ensure_tasks_folder
61
+ io.exec('mkdir .webbynode/tasks') unless io.directory?(".webbynode/tasks")
62
+ %w[before_push after_push].each do |file|
63
+ io.exec("touch .webbynode/tasks/#{file}") unless io.file_exists?(".webbynode/tasks/#{file}")
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ # Gets invoked if the user specified [add] for @action
70
+ def add
71
+ append_task(command)
72
+ write_tasks
73
+ show_tasks
74
+ end
75
+
76
+ # Gets invoked if the user specified [remove] for @action
77
+ def remove
78
+ delete_task(command.to_i)
79
+ write_tasks
80
+ show_tasks
81
+ end
82
+
83
+ # Gets invoked if the user specified [show] for @action
84
+ def show
85
+ show_tasks
86
+ end
87
+
88
+ # Appends a task to the session_tasks method.
89
+ def append_task(task)
90
+ @session_tasks << task
91
+ end
92
+
93
+ # Overwrites the existing file with the new tasks list
94
+ # This will always be done from the session_tasks.
95
+ def write_tasks
96
+ io.open_file(session_file, "w") do |file|
97
+ session_tasks.each do |task|
98
+ file.write "#{task}\n"
99
+ end
100
+ end
101
+ end
102
+
103
+ # Removes a task based on the number(index) provided.
104
+ # example: delete_task(2)
105
+ # This will remove whatever task is 3rd in the session_tasks array.
106
+ def delete_task(i)
107
+ filtered_tasks = []
108
+ session_tasks.each_with_index do |task, index|
109
+ filtered_tasks << task unless index.eql?(i)
110
+ end
111
+ @session_tasks = filtered_tasks
112
+ end
113
+
114
+ # Reads out each task (in order) either from the session_tasks method
115
+ # or straight from the physical file on the filesystem and outputs it directly
116
+ # in the console to the user for feedback.
117
+ def show_tasks(from_file = false)
118
+ read_tasks(session_file, true) if from_file
119
+ if session_tasks.empty?
120
+ io.log_and_exit "You haven't set up any tasks for \"#{type.gsub('_',' ').capitalize}\"."
121
+ end
122
+ io.log "These are the current tasks for \"#{type.gsub('_',' ').capitalize}\":"
123
+ session_tasks.each_with_index do |task, index|
124
+ io.log "[#{index}] #{task}"
125
+ end
126
+ end
127
+
128
+ # Determines the selected file that will be used for the current session.
129
+ # This will be stored inside the session_file method.
130
+ def set_session_file
131
+ case @type
132
+ when 'before_push' then sf = BeforePushTasksFile
133
+ when 'after_push' then sf = AfterPushTasksFile
134
+ end
135
+ @session_file = sf
136
+ end
137
+
138
+ # Parses the parameters and stores the params inside 3 different methods
139
+ # [action] represents: [add/remove/show]
140
+ # [type] represents: [before/after_create before/after_push]
141
+ # [command] represents: The remote command that should get executed
142
+ def parse_parameters
143
+ @action = param(:action)
144
+ @type = param(:type)
145
+ @command = param(:command).join(" ")
146
+ end
147
+
148
+ end
149
+ end
@@ -0,0 +1,8 @@
1
+ module Webbynode::Commands
2
+ class Version < Webbynode::Command
3
+ summary "Displays current version of Webbynode Gem"
4
+ def execute
5
+ io.log("Rapid Deployment Gem v#{Webbynode::VERSION}", true)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,30 @@
1
+ module Webbynode::Commands
2
+ class Webbies < Webbynode::Command
3
+ summary "Lists the Webbies you currently own"
4
+
5
+ def execute
6
+ puts "Fetching list of your Webbies..."
7
+ puts ""
8
+
9
+ header = " "
10
+ header << "Webbies".ljust(16)
11
+ header << "IP".ljust(16)
12
+ header << "Node".ljust(10)
13
+ header << "Plan".ljust(16)
14
+ header << "Status".ljust(15)
15
+
16
+ puts header
17
+
18
+ api.webbies.each_pair do |name, webby|
19
+ str = " "
20
+ str << name.ljust(16)
21
+ str << webby[:ip].ljust(16)
22
+ str << webby[:node].ljust(10)
23
+ str << webby[:plan].ljust(16)
24
+ str << webby[:status]
25
+
26
+ puts str
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,112 @@
1
+ module Webbynode
2
+ class GitError < StandardError; end
3
+ class GitNotRepoError < StandardError; end
4
+ class GitRemoteDoesNotExistError < StandardError; end
5
+ class GitRemoteAlreadyExistsError < StandardError; end
6
+
7
+ class Git
8
+ attr_accessor :config, :remote_ip
9
+
10
+ def present?
11
+ io.directory?(".git")
12
+ end
13
+
14
+ def io
15
+ @@io ||= Webbynode::Io.new
16
+ end
17
+
18
+ def init
19
+ exec("git init") do |output|
20
+ # this indicates init was properly executed
21
+ output =~ /^Initialized empty Git repository in/
22
+ end
23
+ end
24
+
25
+ def clean?
26
+ io.exec("git status") =~ /working directory clean/
27
+ end
28
+
29
+ def delete_file(file)
30
+ return unless io.file_exists?(file)
31
+ io.delete_file(file)
32
+ exec "git rm #{file} > /dev/null 2>&1"
33
+ end
34
+
35
+ def add_git_ignore
36
+ io.create_from_template(".gitignore", "gitignore")
37
+ end
38
+
39
+ def add(what)
40
+ exec "git add #{what}"
41
+ end
42
+
43
+ def add_remote(name, host, repo)
44
+ exec("git remote add #{name} git@#{host}:#{repo}") do |output|
45
+ # raise an exception if remote already exists
46
+ raise GitRemoteAlreadyExistsError, output if output =~ /remote \w+ already exists/
47
+
48
+ # success if output is empty
49
+ output.nil? or output.empty?
50
+ end
51
+ end
52
+
53
+ def commit(comments)
54
+ comments.gsub! /"/, '\"'
55
+ exec("git commit -m \"#{comments}\"") do |output|
56
+ output =~ /#{comments}/ or output =~ /nothing to commit/
57
+ end
58
+ end
59
+
60
+ def parse_config
61
+ return @config if defined?(@config)
62
+ if present? and remote_webbynode?
63
+ config = {}
64
+ current = {}
65
+ File.open(".git/config").each_line do |line|
66
+ case line
67
+ when /^\[(\w+)(?: "(.+)")*\]/
68
+ key, subkey = $1, $2
69
+ current = (config[key] ||= {})
70
+ current = (current[subkey] ||= {}) if subkey
71
+ else
72
+ key, value = line.strip.split(' = ')
73
+ current[key] = value
74
+ end
75
+ end
76
+ @config = config
77
+ else
78
+ raise Webbynode::GitNotRepoError, "Git repository does not exist." unless present?
79
+ raise Webbynode::GitRemoteDoesNotExistError, "Webbynode has not been initialized." unless remote_webbynode?
80
+ end
81
+ end
82
+
83
+ def parse_remote_ip
84
+ @config ||= parse_config
85
+ @remote_ip ||= ($2 if @config["remote"]["webbynode"]["url"] =~ /^(\w+)@(.+):(.+)$/) if @config
86
+ end
87
+
88
+ def remote_webbynode?
89
+ return true if io.exec('git remote') =~ /webbynode/
90
+ false
91
+ end
92
+
93
+ private
94
+
95
+ def exec(cmd, &blk)
96
+ handle_output io.exec(cmd), &blk
97
+ end
98
+
99
+ def handle_output(output, &blk)
100
+ raise GitNotRepoError, output if output =~ /Not a git repository/
101
+
102
+ if blk
103
+ raise GitError, output unless blk.call(output)
104
+ else
105
+ raise GitError, output unless output.nil? or output.empty?
106
+ end
107
+
108
+ true
109
+ end
110
+
111
+ end
112
+ end