webmake 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/lib/ftp.rb +175 -0
  2. data/lib/wake.rb +149 -0
  3. data/sample/Wakefile +20 -0
  4. data/wake +15 -0
  5. metadata +105 -0
@@ -0,0 +1,175 @@
1
+ require 'net/ftp'
2
+ require 'net/sftp'
3
+
4
+ class Ftp
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ connect
9
+ end
10
+
11
+ def connect
12
+ if @config["sftp"]
13
+ @connection = Net::SFTP.start(@config["host"], @config["username"], :password => @config["password"])
14
+ else
15
+ @connection = Net::FTP.new(@config["host"])
16
+ @connection.login(@config["username"], @config["password"])
17
+ end
18
+
19
+ true
20
+ rescue
21
+ false
22
+ end
23
+
24
+ def close
25
+ @connection.close unless @config["sftp"]
26
+ true
27
+ end
28
+
29
+ def connected?
30
+ ! @connection.nil?
31
+ end
32
+
33
+ def list(dir)
34
+ if @config["sftp"]
35
+ files = []
36
+ @connection.dir.foreach(dir) do |entry|
37
+ permissions = entry.longname.split(" ")[0]
38
+ files << [entry.name, !entry.longname.starts_with("d"), permissions_from_string(permissions)] unless [".", ".."].include? entry.name
39
+ end
40
+ files
41
+ else
42
+ @connection.chdir(dir)
43
+ list = []
44
+ @connection.ls("-a").each do |file|
45
+ if file.size() > 8
46
+ filename = ""
47
+ filehold = file.split(" ")[8..-1] # get filename or directory name
48
+ filehold.each {|name_segment| filename += " #{name_segment}" }
49
+ filename.lstrip!
50
+
51
+ # if this is a directory a d or ASCII 100 will be the first character
52
+ # 45 means its just a file
53
+ # anything else is garbage
54
+ unless [".", ".."].include? filename
55
+ if file[0] == 45
56
+ list << [filename, true]
57
+ elsif file[0] == 100
58
+ list << [filename, false]
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ list
65
+ end
66
+ rescue
67
+ []
68
+ end
69
+
70
+ def publish(path, create_dirs_on_failure = true)
71
+ remote_path = prepare_remote_path(path)
72
+
73
+ if @config["sftp"]
74
+ @connection.upload!(path, remote_path)
75
+ else
76
+ @connection.put(path, remote_path)
77
+ end
78
+
79
+ true
80
+ rescue
81
+ if create_dirs_on_failure
82
+ create_directories(remote_path)
83
+ return publish(path, false)
84
+ end
85
+
86
+ false
87
+ end
88
+
89
+ def create_directories(path)
90
+ segments = File.dirname(path).gsub(/^\/?(.+?)\/?$/, "\\1").split("/")
91
+
92
+ dir = ""
93
+ dir = "/" if path[0, 1] == "/"
94
+
95
+ segments.each do |segment|
96
+ dir << "#{segment}/"
97
+
98
+ begin
99
+ @connection.mkdir(dir)
100
+ rescue
101
+ # This dir already exist, move on.
102
+ end
103
+ end
104
+ end
105
+
106
+ def create_dir(path)
107
+ path = prepare_remote_path(path)
108
+
109
+ create_directories(path)
110
+ @connection.mkdir(path)
111
+ true
112
+ rescue
113
+ false
114
+ end
115
+
116
+ def delete_file(path)
117
+ path = prepare_remote_path(path) unless path.match(/^#{Regexp.escape(@config["root"])}/)
118
+
119
+ if @config["sftp"]
120
+ @connection.remove!(path)
121
+ true
122
+ else
123
+ @connection.delete(path)
124
+ true
125
+ end
126
+ rescue
127
+ false
128
+ end
129
+
130
+ def delete_dir(path)
131
+ path = prepare_remote_path(path) unless path.match(/^#{Regexp.escape(@config["root"])}/)
132
+
133
+ if @config["sftp"]
134
+ @connection.dir.entries(path).each do |entry|
135
+ next if entry.name == '.' or entry.name == '..'
136
+
137
+ child_path = File.join(path, entry.name)
138
+
139
+ if @connection.stat!(child_path).directory?
140
+ delete_dir(child_path)
141
+ else
142
+ delete_file(child_path)
143
+ end
144
+ end
145
+
146
+ @connection.rmdir!(path)
147
+ true
148
+ else
149
+ file_list = list(path)
150
+
151
+ file_list.each do |thefile|
152
+ filename = thefile[0]
153
+ # Files are True
154
+ if thefile[1]
155
+ delete_file(File.join(path, filename))
156
+ else
157
+ if filename != "." and filename != ".."
158
+ delete_dir(File.join(path, filename))
159
+ end
160
+ end
161
+ end
162
+
163
+ @connection.rmdir(path)
164
+ true
165
+ end
166
+ rescue
167
+ false
168
+ end
169
+
170
+ def prepare_remote_path(local_path)
171
+ path = local_path.gsub(/^#{Regexp.escape(Dir.pwd)}\//, "")
172
+ File.join(@config["root"], path)
173
+ end
174
+
175
+ end
@@ -0,0 +1,149 @@
1
+ require 'rubygems'
2
+ require 'find'
3
+ require 'yaml'
4
+ require "#{File.expand_path("..", __FILE__)}/ftp"
5
+ require 'fileutils'
6
+
7
+ module Wake
8
+
9
+ def self.public_methods
10
+ ['help', 'init', 'sync', 'set_synced']
11
+ end
12
+
13
+ # ----------------------------------------------------------------------
14
+ # Callable methods
15
+ # ----------------------------------------------------------------------
16
+
17
+ def self.help(e = nil)
18
+ puts "Wake only has three basic commands."
19
+ puts " init - Initializes a new Wake project and creates a sample Wakefile."
20
+ puts " sync - Publishes any changes in the filetree (including deletes)."
21
+ puts " set_synced - Set the project as synchronized without publishing changes."
22
+ end
23
+
24
+ def self.init(e = nil)
25
+ config_file = File.join(Dir.pwd, "Wakefile")
26
+ release_dir = File.join(Dir.pwd, ".wake")
27
+
28
+ FileUtils.mkdir release_dir unless File.exists? release_dir
29
+
30
+ unless File.exists? config_file
31
+ wakefile_sample = File.expand_path("..", __FILE__)+"/../sample/Wakefile"
32
+ FileUtils.cp wakefile_sample, Dir.pwd
33
+ end
34
+ end
35
+
36
+ def self.set_synced(environment = "development")
37
+ self.get_tree_diff(environment)
38
+ puts "Environment #{environment} is now set as synced."
39
+ end
40
+
41
+ def self.sync(environment = "development")
42
+ config_file = File.join(Dir.pwd, "Wakefile")
43
+
44
+ unless File.exists? config_file
45
+ puts "This project has no Wakefile."
46
+ return
47
+ end
48
+
49
+ config = YAML::load(File.open(config_file).read)
50
+
51
+ unless config.has_key? environment
52
+ puts "#{environment.capitalize} environment is not defined in Wakefile."
53
+ end
54
+
55
+ synchronize(environment, config[environment])
56
+ end
57
+
58
+ def self.synchronize(environment, config)
59
+ diff_tree = self.get_tree_diff(environment)
60
+
61
+ ftp = Ftp.new(config)
62
+ diff_tree.each do |filename, stats|
63
+ if stats[:is_dir]
64
+ ftp.delete_dir(filename) if stats[:status] == :delete
65
+ ftp.create_dir(filename) if stats[:status] == :publish
66
+ else
67
+ ftp.delete_file(filename) if stats[:status] == :delete
68
+ ftp.publish(filename) if stats[:status] == :publish
69
+ end
70
+ end
71
+ end
72
+
73
+ # ----------------------------------------------------------------------
74
+ # "Private" methods
75
+ # ----------------------------------------------------------------------
76
+
77
+ def self.get_tree_diff(environment)
78
+ old_tree = self.get_old_tree(environment)
79
+ current_tree = self.get_current_tree
80
+
81
+ last_release = self.get_last_release_date(environment)
82
+
83
+ diff_tree = {}
84
+
85
+ # Find out if any files have been added or updated since the last release.
86
+ current_tree.each do |filename, is_dir|
87
+ if File.ctime(filename) > last_release
88
+ diff_tree[filename] = {
89
+ :is_dir => is_dir,
90
+ :status => :publish
91
+ }
92
+ end
93
+ end
94
+
95
+ # Find out if any files have been removed since the last release.
96
+ old_tree.each do |filename, is_dir|
97
+ unless current_tree.has_key? filename
98
+ diff_tree[filename] = {
99
+ :is_dir => is_dir,
100
+ :status => :delete
101
+ }
102
+ end
103
+ end
104
+
105
+ self.write_tree(environment, current_tree)
106
+
107
+ diff_tree
108
+ end
109
+
110
+ def self.get_last_release_date(environment)
111
+ tree_file = File.join(Dir.pwd, ".wake/#{environment}")
112
+
113
+ if File.exists? tree_file
114
+ return File.ctime tree_file
115
+ end
116
+
117
+ Time.at(0)
118
+ end
119
+
120
+ def self.get_old_tree(environment)
121
+ tree = {}
122
+
123
+ tree_file = File.join(Dir.pwd, ".wake/#{environment}")
124
+ if File.exists? tree_file
125
+ tree = YAML::load(File.open(tree_file).read)
126
+ end
127
+
128
+ tree
129
+ end
130
+
131
+ def self.get_current_tree
132
+ tree = {}
133
+
134
+ path = Dir.pwd
135
+ Find.find(path) do |file|
136
+ tree[file] = File.directory? file unless (file == path or file.match(/(Wakefile|\.wake)/))
137
+ end
138
+
139
+ tree
140
+ end
141
+
142
+ def self.write_tree(environment, tree)
143
+ tree_file = File.join(Dir.pwd, ".wake/#{environment}")
144
+ File.open(tree_file, 'w') do |f|
145
+ f.write YAML::dump(tree)
146
+ end
147
+ end
148
+
149
+ end
@@ -0,0 +1,20 @@
1
+ development:
2
+ sftp: false
3
+ host: "ftp.example.com"
4
+ username: "dev"
5
+ password: "dev"
6
+ root: "/var/www/dev/"
7
+
8
+ test:
9
+ sftp: false
10
+ host: "ftp.example.com"
11
+ username: "test"
12
+ password: "test"
13
+ root: "/var/www/test/"
14
+
15
+ production:
16
+ sftp: true
17
+ host: "sftp.example.com"
18
+ username: "prod"
19
+ password: "prod"
20
+ root: "/var/www/production/"
data/wake ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require "#{File.expand_path("..", __FILE__)}/lib/wake"
3
+
4
+ unless ARGV[0].nil?
5
+ command = ARGV[0]
6
+
7
+ if Wake.public_methods.include? command
8
+ environment = "development"
9
+ environment = ARGV[1] if ["development", "test", "production"].include? ARGV[1]
10
+
11
+ Wake.send(command, environment)
12
+ else
13
+ puts "Wake does not respond to \"#{command}\". Run \"wake help\" for more information."
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webmake
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 0
10
+ version: 1.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Christoffer Lejdborg
14
+ autorequire:
15
+ bindir: .
16
+ cert_chain: []
17
+
18
+ date: 2011-01-21 00:00:00 +01:00
19
+ default_executable: wake
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: net-ssh
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 11
30
+ segments:
31
+ - 2
32
+ - 1
33
+ - 0
34
+ version: 2.1.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: net-sftp
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 5
46
+ segments:
47
+ - 2
48
+ - 0
49
+ - 5
50
+ version: 2.0.5
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ description: Wake is a very simplistic synchronisation tool for web applications. You can set up three envioronments (test, development and production) and use wither FTP or SFTP to sync your sites.
54
+ email: hello@9muses.se
55
+ executables:
56
+ - wake
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - lib/ftp.rb
63
+ - lib/wake.rb
64
+ - sample/Wakefile
65
+ - ./wake
66
+ has_rdoc: true
67
+ homepage: http://github.com/Lejdborg/Wake
68
+ licenses: []
69
+
70
+ post_install_message:
71
+ rdoc_options: []
72
+
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 57
81
+ segments:
82
+ - 1
83
+ - 8
84
+ - 7
85
+ version: 1.8.7
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 23
92
+ segments:
93
+ - 1
94
+ - 3
95
+ - 6
96
+ version: 1.3.6
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.4.2
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: FTP/SFTP sync tool.
104
+ test_files: []
105
+