dandelion 0.1.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler'
2
+
3
+ Bundler::GemHelper.install_tasks
data/bin/dandelion ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
4
+
5
+ require 'dandelion'
6
+
7
+ Dandelion.run
data/dandelion.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require 'dandelion/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'dandelion'
7
+ s.version = Dandelion::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Scott Nelson']
10
+ s.email = ['scottbnel@gmail.com']
11
+ s.homepage = 'http://github.com/scottbnel/dandelion'
12
+ s.summary = "dandelion-#{s.version}"
13
+ s.description = 'Git repository deployment via SFTP'
14
+
15
+ s.add_dependency 'net-sftp', '>= 2.0.5'
16
+ s.add_dependency 'grit', '>= 2.4.1'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ['lib']
22
+ end
data/lib/dandelion.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'dandelion/deployment'
2
+ require 'dandelion/service'
3
+ require 'yaml'
4
+
5
+ module Dandelion
6
+ class << self
7
+ def run
8
+ unless File.exists? '.git'
9
+ puts 'Not a git repository: .git'
10
+ exit
11
+ end
12
+
13
+ unless File.exists? 'deploy.yml'
14
+ puts 'Could not find file: deploy.yml'
15
+ exit
16
+ end
17
+
18
+ config = YAML.load_file 'deploy.yml'
19
+
20
+ if config['scheme'] == 'sftp'
21
+ service = Service::SFTP.new(config['host'], config['username'], config['password'], config['path'])
22
+ else
23
+ puts "Unsupported scheme: #{config['scheme']}"
24
+ exit
25
+ end
26
+
27
+ puts "Connecting to: #{service.uri}"
28
+
29
+ begin
30
+ # Deploy changes since remote revision
31
+ deployment = Deployment::DiffDeployment.new('.', service, config['exclude'])
32
+
33
+ puts "Remote revision: #{deployment.remote_revision}"
34
+ puts "Local revision: #{deployment.local_revision}"
35
+
36
+ deployment.deploy
37
+ rescue Deployment::RemoteRevisionError
38
+ # No remote revision, deploy everything
39
+ deployment = Deployment::FullDeployment.new('.', service, config['exclude'])
40
+
41
+ puts "Remote revision: ---"
42
+ puts "Local revision: #{deployment.local_revision}"
43
+
44
+ deployment.deploy
45
+ end
46
+
47
+ puts "Deployment complete"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,79 @@
1
+ require 'dandelion/git'
2
+
3
+ module Deployment
4
+ class RemoteRevisionError < StandardError; end
5
+
6
+ class Deployment
7
+ def initialize(dir, service, exclude = nil, revision = 'HEAD')
8
+ @service = service
9
+ @exclude = exclude || []
10
+ @tree = Git::Tree.new(dir, revision)
11
+ end
12
+
13
+ def local_revision
14
+ @tree.revision
15
+ end
16
+
17
+ def remote_uri
18
+ @service.uri
19
+ end
20
+
21
+ def write_revision
22
+ @service.write('.revision', local_revision)
23
+ end
24
+ end
25
+
26
+ class DiffDeployment < Deployment
27
+ def initialize(dir, service, exclude = nil, revision = 'HEAD')
28
+ super(dir, service, exclude, revision)
29
+ @diff = Git::Diff.new(dir, read_revision)
30
+ end
31
+
32
+ def remote_revision
33
+ @diff.revision
34
+ end
35
+
36
+ def deploy
37
+ if remote_revision != local_revision
38
+ @diff.changed.each do |file|
39
+ unless @exclude.include?(file)
40
+ puts "Uploading file: #{file}"
41
+ @service.write(file, @tree.show(file))
42
+ end
43
+ end
44
+ @diff.deleted.each do |file|
45
+ unless @exclude.include?(file)
46
+ puts "Deleting file: #{file}"
47
+ @service.delete(file)
48
+ end
49
+ end
50
+ write_revision
51
+ else
52
+ puts "Nothing to deploy"
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def read_revision
59
+ begin
60
+ @service.read('.revision').chomp
61
+ rescue Net::SFTP::StatusException => e
62
+ raise unless e.code == 2
63
+ raise RemoteRevisionError
64
+ end
65
+ end
66
+ end
67
+
68
+ class FullDeployment < Deployment
69
+ def deploy
70
+ @tree.files.each do |file|
71
+ unless @exclude.include?(file)
72
+ puts "Uploading file: #{file}"
73
+ @service.write(file, @tree.show(file))
74
+ end
75
+ end
76
+ write_revision
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,51 @@
1
+ require 'grit'
2
+
3
+ module Git
4
+ class Diff
5
+ attr_reader :revision
6
+
7
+ def initialize(dir, revision)
8
+ @revision = revision
9
+ @raw = `cd #{dir}; git diff --name-status #{@revision} HEAD`
10
+ end
11
+
12
+ def changed
13
+ files_flagged ['A', 'C', 'M']
14
+ end
15
+
16
+ def deleted
17
+ files_flagged ['D']
18
+ end
19
+
20
+ private
21
+
22
+ def files_flagged(statuses)
23
+ items = []
24
+ @raw.split("\n").each do |line|
25
+ status, file = line.split("\t")
26
+ items << file if statuses.include? status
27
+ end
28
+ items
29
+ end
30
+ end
31
+
32
+ class Tree
33
+ def initialize(dir, revision)
34
+ @dir = dir
35
+ @commit = Grit::Repo.new(dir).commit(revision)
36
+ @tree = @commit.tree
37
+ end
38
+
39
+ def files
40
+ `cd #{@dir}; git ls-tree --name-only -r #{revision}`.split("\n")
41
+ end
42
+
43
+ def show(file)
44
+ (@tree / file).data
45
+ end
46
+
47
+ def revision
48
+ @commit.sha
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,79 @@
1
+ require 'net/sftp'
2
+ require 'tempfile'
3
+
4
+ module Service
5
+ class Service
6
+ def initialize(host, username, path)
7
+ @host = host
8
+ @username = username
9
+ @path = path
10
+ end
11
+
12
+ def uri
13
+ "#{@scheme}://#{@username}@#{@host}/#{@path}"
14
+ end
15
+ end
16
+
17
+ class SFTP < Service
18
+ def initialize(host, username, password, path)
19
+ super(host, username, path)
20
+ @scheme = 'sftp'
21
+ @sftp = Net::SFTP.start(host, username, :password => password)
22
+ end
23
+
24
+ def read(file)
25
+ @sftp.file.open(File.join(@path, file), 'r') do |f|
26
+ f.gets
27
+ end
28
+ end
29
+
30
+ def write(file, data)
31
+ path = File.join(@path, file)
32
+ begin
33
+ dir = File.dirname(path)
34
+ @sftp.stat!(dir)
35
+ rescue Net::SFTP::StatusException => e
36
+ raise unless e.code == 2
37
+ mkdir_p(dir)
38
+ end
39
+ tmp = Tempfile.new(file.gsub('/', '.'))
40
+ tmp << data
41
+ tmp.flush
42
+ @sftp.upload!(tmp.path, path)
43
+ tmp.close
44
+ end
45
+
46
+ def delete(file)
47
+ path = File.join(@path, file)
48
+ @sftp.remove!(path)
49
+ cleanup(File.dirname(path))
50
+ end
51
+
52
+ private
53
+
54
+ def cleanup(dir)
55
+ unless File.identical?(dir, @path)
56
+ if empty?(dir)
57
+ @sftp.rmdir!(dir)
58
+ cleanup(File.dirname(dir))
59
+ end
60
+ end
61
+ end
62
+
63
+ def empty?(dir)
64
+ @sftp.dir.entries(dir).map do |entry|
65
+ entry.name unless entry.name == '.' or entry.name == '..'
66
+ end.compact.empty?
67
+ end
68
+
69
+ def mkdir_p(dir)
70
+ begin
71
+ @sftp.mkdir!(dir)
72
+ rescue Net::SFTP::StatusException => e
73
+ raise unless e.code == 2
74
+ mkdir_p(File.dirname(dir))
75
+ mkdir_p(dir)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ module Dandelion
2
+ VERSION = '0.1.1'
3
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dandelion
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Scott Nelson
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-21 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: net-sftp
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 0
31
+ - 5
32
+ version: 2.0.5
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: grit
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 2
45
+ - 4
46
+ - 1
47
+ version: 2.4.1
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description: Git repository deployment via SFTP
51
+ email:
52
+ - scottbnel@gmail.com
53
+ executables:
54
+ - dandelion
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - .gitignore
61
+ - Gemfile
62
+ - Rakefile
63
+ - bin/dandelion
64
+ - dandelion.gemspec
65
+ - lib/dandelion.rb
66
+ - lib/dandelion/deployment.rb
67
+ - lib/dandelion/git.rb
68
+ - lib/dandelion/service.rb
69
+ - lib/dandelion/version.rb
70
+ has_rdoc: true
71
+ homepage: http://github.com/scottbnel/dandelion
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options: []
76
+
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ requirements: []
96
+
97
+ rubyforge_project:
98
+ rubygems_version: 1.3.7
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: dandelion-0.1.1
102
+ test_files: []
103
+