dandelion 0.1.4 → 0.1.5
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.
- data/README.md +28 -12
- data/bin/dandelion +2 -2
- data/lib/dandelion.rb +13 -55
- data/lib/dandelion/cli.rb +150 -0
- data/lib/dandelion/deployment.rb +97 -71
- data/lib/dandelion/git.rb +52 -43
- data/lib/dandelion/service.rb +68 -66
- data/lib/dandelion/version.rb +1 -1
- metadata +3 -2
data/README.md
CHANGED
@@ -13,26 +13,42 @@ Alternatively, you can build the gem yourself:
|
|
13
13
|
$ cd dandelion
|
14
14
|
$ rake install
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
argument.
|
16
|
+
Config
|
17
|
+
------
|
18
|
+
Configuration options are specified in a YAML file (Dandelion looks for a file
|
19
|
+
named `dandelion.yml` by default):
|
21
20
|
|
21
|
+
# Required
|
22
22
|
scheme: sftp
|
23
23
|
host: example.com
|
24
24
|
username: user
|
25
25
|
password: pass
|
26
26
|
path: path/to/deployment
|
27
27
|
|
28
|
+
# Optional
|
28
29
|
exclude:
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
- .gitignore
|
31
|
+
- dandelion.yml
|
32
|
+
|
33
|
+
Usage
|
34
|
+
-----
|
35
|
+
From the root directory of a Git repository, run:
|
33
36
|
|
34
37
|
$ dandelion
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
Or:
|
40
|
+
|
41
|
+
$ dandelion path/to/config.yml
|
42
|
+
|
43
|
+
This will deploy the local `HEAD` revision to the server specified in the config
|
44
|
+
file. Dandelion keeps track of the most recently deployed revision so that only
|
45
|
+
files which have changed since the last deployment need to be transferred.
|
46
|
+
|
47
|
+
For a more complete summary of usage options, run:
|
48
|
+
|
49
|
+
$ dandelion -h
|
50
|
+
Usage: dandelion [options] [config_file]
|
51
|
+
-f, --force Force deployment
|
52
|
+
-s, --status Display revision status
|
53
|
+
-v, --version Display the current version
|
54
|
+
-h, --help Display this screen
|
data/bin/dandelion
CHANGED
data/lib/dandelion.rb
CHANGED
@@ -1,60 +1,18 @@
|
|
1
|
-
require 'dandelion/deployment'
|
2
|
-
require 'dandelion/service'
|
3
|
-
require 'yaml'
|
4
|
-
|
5
1
|
module Dandelion
|
6
2
|
class << self
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
puts "Could not find file: #{config_file}"
|
21
|
-
exit
|
22
|
-
end
|
23
|
-
|
24
|
-
config = YAML.load_file config_file
|
25
|
-
|
26
|
-
if config['scheme'] == 'sftp'
|
27
|
-
service = Service::SFTP.new(config['host'], config['username'], config['password'], config['path'])
|
28
|
-
else
|
29
|
-
puts "Unsupported scheme: #{config['scheme']}"
|
30
|
-
exit
|
31
|
-
end
|
32
|
-
|
33
|
-
puts "Connecting to: #{service.uri}"
|
34
|
-
|
35
|
-
begin
|
36
|
-
begin
|
37
|
-
# Deploy changes since remote revision
|
38
|
-
deployment = Deployment::DiffDeployment.new('.', service, config['exclude'])
|
39
|
-
|
40
|
-
puts "Remote revision: #{deployment.remote_revision}"
|
41
|
-
puts "Local revision: #{deployment.local_revision}"
|
42
|
-
|
43
|
-
deployment.deploy
|
44
|
-
rescue Service::RemoteRevisionError
|
45
|
-
# No remote revision, deploy everything
|
46
|
-
deployment = Deployment::FullDeployment.new('.', service, config['exclude'])
|
47
|
-
|
48
|
-
puts "Remote revision: ---"
|
49
|
-
puts "Local revision: #{deployment.local_revision}"
|
50
|
-
|
51
|
-
deployment.deploy
|
52
|
-
end
|
53
|
-
|
54
|
-
puts "Deployment complete"
|
55
|
-
rescue Git::DiffError
|
56
|
-
puts "Failed to deploy"
|
57
|
-
puts "Try merging remote changes before deploying again"
|
3
|
+
def logger
|
4
|
+
return @log if @log
|
5
|
+
@log = Logger.new(STDOUT)
|
6
|
+
@log.level = Logger::INFO
|
7
|
+
@log.formatter = formatter
|
8
|
+
@log
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def formatter
|
14
|
+
proc do |severity, datetime, progname, msg|
|
15
|
+
"#{msg}\n"
|
58
16
|
end
|
59
17
|
end
|
60
18
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'dandelion'
|
2
|
+
require 'dandelion/deployment'
|
3
|
+
require 'dandelion/git'
|
4
|
+
require 'dandelion/service'
|
5
|
+
require 'dandelion/version'
|
6
|
+
require 'optparse'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module Dandelion
|
10
|
+
module Cli
|
11
|
+
class UnsupportedSchemeError < StandardError; end
|
12
|
+
|
13
|
+
class Options
|
14
|
+
def initialize
|
15
|
+
@options = {}
|
16
|
+
@optparse = OptionParser.new do |opts|
|
17
|
+
opts.banner = 'Usage: dandelion [options] [config_file]'
|
18
|
+
|
19
|
+
@options[:force] = false
|
20
|
+
opts.on('-f', '--force', 'Force deployment') do
|
21
|
+
@options[:force] = true
|
22
|
+
end
|
23
|
+
|
24
|
+
@options[:status] = false
|
25
|
+
opts.on('-s', '--status', 'Display revision status') do
|
26
|
+
@options[:status] = true;
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-v', '--version', 'Display the current version') do
|
30
|
+
puts "Dandelion v#{Dandelion::VERSION}"
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('-h', '--help', 'Display this screen') do
|
35
|
+
puts opts
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse!(args)
|
42
|
+
@args = args
|
43
|
+
@optparse.parse!(@args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def config_file
|
47
|
+
if @args[0]
|
48
|
+
@args[0].strip
|
49
|
+
else
|
50
|
+
'dandelion.yml'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def [](key)
|
55
|
+
@options[key]
|
56
|
+
end
|
57
|
+
|
58
|
+
def []=(key, value)
|
59
|
+
@options[key] = value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Main
|
64
|
+
class << self
|
65
|
+
def execute(args)
|
66
|
+
new(args).execute!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(args)
|
71
|
+
@options = Options.new
|
72
|
+
@options.parse!(args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def log
|
76
|
+
Dandelion.logger
|
77
|
+
end
|
78
|
+
|
79
|
+
def check_files!
|
80
|
+
unless File.exists? '.git'
|
81
|
+
log.fatal('Not a git repository: .git')
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
unless File.exists? @options.config_file
|
85
|
+
log.fatal("Could not find file: #{@options.config_file}")
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def service(config)
|
91
|
+
if config['scheme'] == 'sftp'
|
92
|
+
Service::SFTP.new(config['host'], config['username'], config['password'], config['path'])
|
93
|
+
else
|
94
|
+
raise UnsupportedSchemeError
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def execute!
|
99
|
+
check_files!
|
100
|
+
config = YAML.load_file @options.config_file
|
101
|
+
|
102
|
+
begin
|
103
|
+
service = service config
|
104
|
+
rescue UnsupportedSchemeError
|
105
|
+
log.fatal("Unsupported scheme: #{config['scheme']}")
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
|
109
|
+
log.info("Connecting to: #{service.uri}")
|
110
|
+
repo = Git::Repo.new('.')
|
111
|
+
|
112
|
+
begin
|
113
|
+
deployment = Deployment::DiffDeployment.new(repo, service, config['exclude'])
|
114
|
+
rescue Deployment::RemoteRevisionError
|
115
|
+
deployment = Deployment::FullDeployment.new(repo, service, config['exclude'])
|
116
|
+
rescue Git::DiffError
|
117
|
+
log.fatal('Error: could not generate diff')
|
118
|
+
log.fatal('Try merging remote changes before running dandelion again')
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
|
122
|
+
begin
|
123
|
+
repo.remote_list.each do |remote|
|
124
|
+
deployment.validate_state(remote)
|
125
|
+
end
|
126
|
+
rescue Deployment::FastForwardError
|
127
|
+
if !@options[:force] and !@options[:status]
|
128
|
+
log.warn('Warning: you are trying to deploy unpushed commits')
|
129
|
+
log.warn('This could potentially prevent others from being able to deploy')
|
130
|
+
log.warn('If you are sure you want to this, use the -f option to force deployment')
|
131
|
+
exit
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
remote_revision = deployment.remote_revision || '---'
|
136
|
+
local_revision = deployment.local_revision
|
137
|
+
|
138
|
+
log.info("Remote revision: #{remote_revision}")
|
139
|
+
log.info("Local revision: #{local_revision}")
|
140
|
+
|
141
|
+
if @options[:status]
|
142
|
+
exit
|
143
|
+
end
|
144
|
+
|
145
|
+
deployment.deploy
|
146
|
+
log.info("Deployment complete")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/dandelion/deployment.rb
CHANGED
@@ -1,100 +1,126 @@
|
|
1
1
|
require 'dandelion/git'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
module Dandelion
|
4
|
+
module Deployment
|
5
|
+
class RemoteRevisionError < StandardError; end
|
6
|
+
class FastForwardError < StandardError; end
|
7
|
+
|
8
|
+
class Deployment
|
9
|
+
def initialize(repo, service, exclude = nil, revision = 'HEAD')
|
10
|
+
@repo = repo
|
11
|
+
@service = service
|
12
|
+
@exclude = exclude || []
|
13
|
+
@tree = Git::Tree.new(@repo, revision)
|
14
|
+
end
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
16
|
+
def local_revision
|
17
|
+
@tree.revision
|
18
|
+
end
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
|
20
|
+
def remote_revision
|
21
|
+
nil
|
22
|
+
end
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
def remote_uri
|
25
|
+
@service.uri
|
26
|
+
end
|
22
27
|
|
23
|
-
|
28
|
+
def write_revision
|
29
|
+
@service.write('.revision', local_revision)
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_state(remote = nil)
|
33
|
+
if remote and @repo.git.native(:remote, {:raise => true}, 'show', remote) =~ /fast-forward/i
|
34
|
+
raise FastForwardError
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def log
|
39
|
+
Dandelion.logger
|
40
|
+
end
|
24
41
|
|
25
|
-
|
26
|
-
return @exclude.map { |e| file.start_with?(e) }.any?
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class DiffDeployment < Deployment
|
31
|
-
def initialize(dir, service, exclude = nil, revision = 'HEAD')
|
32
|
-
super(dir, service, exclude, revision)
|
33
|
-
@diff = Git::Diff.new(dir, read_revision)
|
34
|
-
end
|
42
|
+
protected
|
35
43
|
|
36
|
-
|
37
|
-
|
44
|
+
def exclude_file?(file)
|
45
|
+
return @exclude.map { |e| file.start_with?(e) }.any?
|
46
|
+
end
|
38
47
|
end
|
48
|
+
|
49
|
+
class DiffDeployment < Deployment
|
50
|
+
def initialize(repo, service, exclude = nil, revision = 'HEAD')
|
51
|
+
super(repo, service, exclude, revision)
|
52
|
+
@diff = Git::Diff.new(@repo, read_remote_revision, revision)
|
53
|
+
end
|
39
54
|
|
40
|
-
|
41
|
-
|
42
|
-
deploy_changed
|
43
|
-
deploy_deleted
|
44
|
-
else
|
45
|
-
puts "Nothing to deploy"
|
46
|
-
end
|
47
|
-
unless revisions_match?
|
48
|
-
write_revision
|
55
|
+
def remote_revision
|
56
|
+
@diff.from_revision
|
49
57
|
end
|
50
|
-
end
|
51
58
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
59
|
+
def deploy
|
60
|
+
if !revisions_match? && any?
|
61
|
+
deploy_changed
|
62
|
+
deploy_deleted
|
56
63
|
else
|
57
|
-
|
58
|
-
|
64
|
+
log.info("Nothing to deploy")
|
65
|
+
end
|
66
|
+
unless revisions_match?
|
67
|
+
write_revision
|
59
68
|
end
|
60
69
|
end
|
61
|
-
end
|
62
70
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
71
|
+
def deploy_changed
|
72
|
+
@diff.changed.each do |file|
|
73
|
+
if exclude_file?(file)
|
74
|
+
log.info("Skipping file: #{file}")
|
75
|
+
else
|
76
|
+
log.info("Uploading file: #{file}")
|
77
|
+
@service.write(file, @tree.show(file))
|
78
|
+
end
|
70
79
|
end
|
71
80
|
end
|
72
|
-
end
|
73
81
|
|
74
|
-
|
75
|
-
|
76
|
-
|
82
|
+
def deploy_deleted
|
83
|
+
@diff.deleted.each do |file|
|
84
|
+
if exclude_file?(file)
|
85
|
+
log.info("Skipping file: #{file}")
|
86
|
+
else
|
87
|
+
log.info("Deleting file: #{file}")
|
88
|
+
@service.delete(file)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
77
92
|
|
78
|
-
|
79
|
-
|
80
|
-
|
93
|
+
def any?
|
94
|
+
@diff.changed.any? || @diff.deleted.any?
|
95
|
+
end
|
96
|
+
|
97
|
+
def revisions_match?
|
98
|
+
remote_revision == local_revision
|
99
|
+
end
|
81
100
|
|
82
|
-
|
101
|
+
private
|
83
102
|
|
84
|
-
|
85
|
-
|
103
|
+
def read_remote_revision
|
104
|
+
begin
|
105
|
+
@service.read('.revision').chomp
|
106
|
+
rescue Service::MissingFileError
|
107
|
+
raise RemoteRevisionError
|
108
|
+
end
|
109
|
+
end
|
86
110
|
end
|
87
|
-
end
|
88
111
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
112
|
+
class FullDeployment < Deployment
|
113
|
+
def deploy
|
114
|
+
@tree.files.each do |file|
|
115
|
+
if exclude_file?(file)
|
116
|
+
log.info("Skipping file: #{file}")
|
117
|
+
else
|
118
|
+
log.info("Uploading file: #{file}")
|
119
|
+
@service.write(file, @tree.show(file))
|
120
|
+
end
|
95
121
|
end
|
122
|
+
write_revision
|
96
123
|
end
|
97
|
-
write_revision
|
98
124
|
end
|
99
125
|
end
|
100
126
|
end
|
data/lib/dandelion/git.rb
CHANGED
@@ -1,60 +1,69 @@
|
|
1
1
|
require 'grit'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
3
|
+
module Dandelion
|
4
|
+
module Git
|
5
|
+
class DiffError < StandardError; end
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@revision = revision
|
11
|
-
@raw = `cd #{dir}; git diff --name-status #{@revision} HEAD`
|
12
|
-
check_state!
|
13
|
-
end
|
14
|
-
|
15
|
-
def changed
|
16
|
-
files_flagged ['A', 'C', 'M']
|
17
|
-
end
|
18
|
-
|
19
|
-
def deleted
|
20
|
-
files_flagged ['D']
|
7
|
+
class Repo < Grit::Repo
|
8
|
+
def initialize(dir)
|
9
|
+
super(dir)
|
10
|
+
end
|
21
11
|
end
|
12
|
+
|
13
|
+
class Diff
|
14
|
+
attr_reader :from_revision, :to_revision
|
15
|
+
|
16
|
+
@files = nil
|
17
|
+
|
18
|
+
def initialize(repo, from_revision, to_revision)
|
19
|
+
@repo = repo
|
20
|
+
@from_revision = from_revision
|
21
|
+
@to_revision = to_revision
|
22
|
+
begin
|
23
|
+
@files = parse_diff @repo.git.native(:diff, {:name_status => true, :raise => true}, from_revision, to_revision)
|
24
|
+
rescue Grit::Git::CommandFailed
|
25
|
+
raise DiffError
|
26
|
+
end
|
27
|
+
end
|
22
28
|
|
23
|
-
|
29
|
+
def changed
|
30
|
+
@files.select { |file, status| ['A', 'C', 'M'].include?(status) }.keys
|
31
|
+
end
|
24
32
|
|
25
|
-
|
26
|
-
|
27
|
-
@raw.split("\n").each do |line|
|
28
|
-
status, file = line.split("\t")
|
29
|
-
items << file if statuses.include? status
|
33
|
+
def deleted
|
34
|
+
@files.select { |file, status| 'D' == status }.keys
|
30
35
|
end
|
31
|
-
|
32
|
-
|
36
|
+
|
37
|
+
private
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
def parse_diff(diff)
|
40
|
+
files = {}
|
41
|
+
diff.split("\n").each do |line|
|
42
|
+
status, file = line.split("\t")
|
43
|
+
files[file] = status
|
44
|
+
end
|
45
|
+
files
|
37
46
|
end
|
38
47
|
end
|
39
|
-
end
|
40
48
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
class Tree
|
50
|
+
def initialize(repo, revision)
|
51
|
+
@repo = repo
|
52
|
+
@commit = @repo.commit(revision)
|
53
|
+
@tree = @commit.tree
|
54
|
+
end
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
56
|
+
def files
|
57
|
+
@repo.git.native(:ls_tree, {:name_only => true}, revision).split("\n")
|
58
|
+
end
|
51
59
|
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
def show(file)
|
61
|
+
(@tree / file).data
|
62
|
+
end
|
55
63
|
|
56
|
-
|
57
|
-
|
64
|
+
def revision
|
65
|
+
@commit.sha
|
66
|
+
end
|
58
67
|
end
|
59
68
|
end
|
60
69
|
end
|
data/lib/dandelion/service.rb
CHANGED
@@ -1,89 +1,91 @@
|
|
1
1
|
require 'net/sftp'
|
2
2
|
require 'tempfile'
|
3
3
|
|
4
|
-
module
|
5
|
-
|
4
|
+
module Dandelion
|
5
|
+
module Service
|
6
|
+
class MissingFileError < StandardError; end
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
class Service
|
9
|
+
def initialize(host, username, path)
|
10
|
+
@host = host
|
11
|
+
@username = username
|
12
|
+
@path = path
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
def uri
|
16
|
+
"#{@scheme}://#{@username}@#{@host}/#{@path}"
|
17
|
+
end
|
16
18
|
end
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
class SFTP < Service
|
21
|
+
def initialize(host, username, password, path)
|
22
|
+
super(host, username, path)
|
23
|
+
@scheme = 'sftp'
|
24
|
+
@sftp = Net::SFTP.start(host, username, :password => password)
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
def read(file)
|
28
|
+
begin
|
29
|
+
@sftp.file.open(File.join(@path, file), 'r') do |f|
|
30
|
+
f.gets
|
31
|
+
end
|
32
|
+
rescue Net::SFTP::StatusException => e
|
33
|
+
raise unless e.code == 2
|
34
|
+
raise MissingFileError
|
30
35
|
end
|
31
|
-
rescue Net::SFTP::StatusException => e
|
32
|
-
raise unless e.code == 2
|
33
|
-
raise RemoteRevisionError
|
34
36
|
end
|
35
|
-
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
def write(file, data)
|
39
|
+
path = File.join(@path, file)
|
40
|
+
begin
|
41
|
+
dir = File.dirname(path)
|
42
|
+
@sftp.stat!(dir)
|
43
|
+
rescue Net::SFTP::StatusException => e
|
44
|
+
raise unless e.code == 2
|
45
|
+
mkdir_p(dir)
|
46
|
+
end
|
47
|
+
tmp = Tempfile.new(file.gsub('/', '.'))
|
48
|
+
tmp << data
|
49
|
+
tmp.flush
|
50
|
+
@sftp.upload!(tmp.path, path)
|
51
|
+
tmp.close
|
45
52
|
end
|
46
|
-
tmp = Tempfile.new(file.gsub('/', '.'))
|
47
|
-
tmp << data
|
48
|
-
tmp.flush
|
49
|
-
@sftp.upload!(tmp.path, path)
|
50
|
-
tmp.close
|
51
|
-
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
def delete(file)
|
55
|
+
begin
|
56
|
+
path = File.join(@path, file)
|
57
|
+
@sftp.remove!(path)
|
58
|
+
cleanup(File.dirname(path))
|
59
|
+
rescue Net::SFTP::StatusException => e
|
60
|
+
raise unless e.code == 2
|
61
|
+
end
|
60
62
|
end
|
61
|
-
end
|
62
63
|
|
63
|
-
|
64
|
+
private
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
def cleanup(dir)
|
67
|
+
unless File.identical?(dir, @path)
|
68
|
+
if empty?(dir)
|
69
|
+
@sftp.rmdir!(dir)
|
70
|
+
cleanup(File.dirname(dir))
|
71
|
+
end
|
70
72
|
end
|
71
73
|
end
|
72
|
-
end
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
def empty?(dir)
|
76
|
+
@sftp.dir.entries(dir).map do |entry|
|
77
|
+
entry.name unless entry.name == '.' or entry.name == '..'
|
78
|
+
end.compact.empty?
|
79
|
+
end
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
81
|
+
def mkdir_p(dir)
|
82
|
+
begin
|
83
|
+
@sftp.mkdir!(dir)
|
84
|
+
rescue Net::SFTP::StatusException => e
|
85
|
+
raise unless e.code == 2
|
86
|
+
mkdir_p(File.dirname(dir))
|
87
|
+
mkdir_p(dir)
|
88
|
+
end
|
87
89
|
end
|
88
90
|
end
|
89
91
|
end
|
data/lib/dandelion/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: dandelion
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Scott Nelson
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- bin/dandelion
|
53
53
|
- dandelion.gemspec
|
54
54
|
- lib/dandelion.rb
|
55
|
+
- lib/dandelion/cli.rb
|
55
56
|
- lib/dandelion/deployment.rb
|
56
57
|
- lib/dandelion/git.rb
|
57
58
|
- lib/dandelion/service.rb
|
@@ -83,6 +84,6 @@ rubyforge_project:
|
|
83
84
|
rubygems_version: 1.5.0
|
84
85
|
signing_key:
|
85
86
|
specification_version: 3
|
86
|
-
summary: dandelion-0.1.
|
87
|
+
summary: dandelion-0.1.5
|
87
88
|
test_files: []
|
88
89
|
|