dandelion 0.3.15 → 0.4.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +11 -0
  5. data/LICENSE +1 -1
  6. data/README.md +15 -26
  7. data/Rakefile +4 -8
  8. data/bin/dandelion +4 -3
  9. data/dandelion.gemspec +23 -20
  10. data/lib/dandelion.rb +26 -10
  11. data/lib/dandelion/adapter.rb +45 -0
  12. data/lib/dandelion/{backend → adapter}/ftp.rb +22 -17
  13. data/lib/dandelion/adapter/noop.rb +14 -0
  14. data/lib/dandelion/{backend → adapter}/s3.rb +14 -15
  15. data/lib/dandelion/{backend → adapter}/sftp.rb +29 -16
  16. data/lib/dandelion/change.rb +11 -0
  17. data/lib/dandelion/changeset.rb +49 -0
  18. data/lib/dandelion/cli.rb +127 -0
  19. data/lib/dandelion/command.rb +26 -92
  20. data/lib/dandelion/command/deploy.rb +54 -41
  21. data/lib/dandelion/command/status.rb +14 -13
  22. data/lib/dandelion/config.rb +36 -0
  23. data/lib/dandelion/deployer.rb +53 -0
  24. data/lib/dandelion/diff.rb +62 -0
  25. data/lib/dandelion/tree.rb +22 -0
  26. data/lib/dandelion/utils.rb +13 -0
  27. data/lib/dandelion/version.rb +2 -2
  28. data/lib/dandelion/workspace.rb +71 -0
  29. data/spec/dandelion/adapter_spec.rb +42 -0
  30. data/spec/dandelion/change_spec.rb +17 -0
  31. data/spec/dandelion/changeset_spec.rb +73 -0
  32. data/spec/dandelion/command/deploy_spec.rb +111 -0
  33. data/spec/dandelion/command/status_spec.rb +4 -0
  34. data/spec/dandelion/command_spec.rb +70 -0
  35. data/spec/dandelion/config_spec.rb +15 -0
  36. data/spec/dandelion/deployer_spec.rb +65 -0
  37. data/spec/dandelion/diff_spec.rb +54 -0
  38. data/spec/dandelion/tree_spec.rb +15 -0
  39. data/spec/dandelion/workspace_spec.rb +77 -0
  40. data/{test/test_git.git → spec/fixtures/repo.git}/HEAD +0 -0
  41. data/{test/test_git.git → spec/fixtures/repo.git}/config +1 -0
  42. data/{test/test_git.git → spec/fixtures/repo.git}/description +0 -0
  43. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/applypatch-msg.sample +0 -0
  44. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/commit-msg.sample +0 -0
  45. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/post-update.sample +0 -0
  46. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/pre-applypatch.sample +0 -0
  47. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/pre-commit.sample +6 -2
  48. data/spec/fixtures/repo.git/hooks/pre-push.sample +53 -0
  49. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/pre-rebase.sample +0 -0
  50. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/prepare-commit-msg.sample +0 -0
  51. data/{test/test_git.git → spec/fixtures/repo.git}/hooks/update.sample +1 -1
  52. data/{test/test_git.git → spec/fixtures/repo.git}/info/exclude +0 -0
  53. data/spec/fixtures/repo.git/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 +0 -0
  54. data/spec/fixtures/repo.git/objects/34/e64fe350f3ea80c989cdac7a99c2adb8574fca +0 -0
  55. data/spec/fixtures/repo.git/objects/3d/9b743acb4a84dd99002d2c6f3fcf1a47e9f06b +0 -0
  56. data/spec/fixtures/repo.git/objects/4e/44973d41d33bf5342037f56497efe0a9604d25 +0 -0
  57. data/{test/test_git.git → spec/fixtures/repo.git}/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 +0 -0
  58. data/spec/fixtures/repo.git/objects/6c/a0f54491390579ce9438ec89c64c6b3499683a +0 -0
  59. data/spec/fixtures/repo.git/objects/8a/e33865a630c5d141c8a498f0c0166ff240b433 +0 -0
  60. data/{test/test_git.git → spec/fixtures/repo.git}/objects/90/2dce0535b19f0c15ac8407fc4468256ad672d7 +0 -0
  61. data/spec/fixtures/repo.git/objects/c3/9af82404cc4267b1ba5f4b4437a511e0776abb +0 -0
  62. data/spec/fixtures/repo.git/objects/c8/85b3f693ed6e2926971ef75680f41b318072ae +0 -0
  63. data/spec/fixtures/repo.git/objects/d8/7cbcba0e2ede0752bdafc5938da35546803ba5 +0 -0
  64. data/spec/fixtures/repo.git/objects/e2/89ff1e2729839759dbd6fe99b6e35880910c7c +0 -0
  65. data/{test/test_git.git → spec/fixtures/repo.git}/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  66. data/{test/test_git.git → spec/fixtures/repo.git}/objects/ea/41dba10b54a794284e0be009a11f0ff3716a28 +0 -0
  67. data/spec/fixtures/repo.git/objects/ee/314a31b622b027c10981acaed7903a3607dbd4 +0 -0
  68. data/spec/fixtures/repo.git/objects/f6/66b137794d56880bab05e8fd256713a8fccf92 +0 -0
  69. data/spec/fixtures/repo.git/refs/heads/master +1 -0
  70. data/spec/spec_helper.rb +28 -0
  71. metadata +119 -127
  72. data/lib/dandelion/application.rb +0 -73
  73. data/lib/dandelion/backend.rb +0 -54
  74. data/lib/dandelion/deployment.rb +0 -173
  75. data/lib/dandelion/git.rb +0 -123
  76. data/test/fixtures/diff +0 -3
  77. data/test/fixtures/ls_tree +0 -5
  78. data/test/test_diff_deployment.rb +0 -122
  79. data/test/test_ftp.rb +0 -49
  80. data/test/test_git.git/hooks/post-commit.sample +0 -8
  81. data/test/test_git.git/hooks/post-receive.sample +0 -15
  82. data/test/test_git.git/objects/0c/a605e9f0f1d42ce8193ac36db11ec3cc9efc08 +0 -0
  83. data/test/test_git.git/objects/11/bada4e36fd065c8d1d3ca97b8dffa496c8e021 +0 -0
  84. data/test/test_git.git/objects/88/d4480861346093048e08ce8dcc577d8aa69379 +0 -1
  85. data/test/test_git.git/objects/a6/394b3e8a82b76b0dd5b6b317f489dfe22426a6 +0 -0
  86. data/test/test_git.git/objects/a6/5140d5ec9f47064f614ecf8e43776baa5c0c11 +0 -0
  87. data/test/test_git.git/objects/f5/5f3c44c89e5d215fbaaef9d33563117fe0b61b +0 -1
  88. data/test/test_git.git/objects/ff/1f1d4bd0c99e1c9cca047c46b2194accf89504 +0 -4
  89. data/test/test_git.git/refs/heads/master +0 -1
  90. data/test/test_git.rb +0 -50
  91. data/test/test_sftp.rb +0 -54
@@ -1,54 +0,0 @@
1
- require 'tempfile'
2
-
3
- module Dandelion
4
- module Backend
5
- class MissingFileError < StandardError; end
6
- class UnsupportedSchemeError < StandardError; end
7
-
8
- class MissingDependencyError < StandardError
9
- attr_reader :gems
10
-
11
- def initialize(gems)
12
- @gems = gems
13
- end
14
- end
15
-
16
- class Base
17
- class << self
18
- @@backends = {}
19
-
20
- def create(config)
21
- Dir.glob(File.join(File.dirname(__FILE__), 'backend', '*.rb')) { |file| require file }
22
- raise UnsupportedSchemeError unless @@backends.include?(config['scheme'])
23
- begin
24
- @@backends[config['scheme']].new(config)
25
- rescue LoadError
26
- raise MissingDependencyError.new(@@backends[config['scheme']].gem_list)
27
- end
28
- end
29
-
30
- def scheme(scheme)
31
- @@backends[scheme] = self
32
- end
33
-
34
- def gems(*gems)
35
- @gems = gems
36
- end
37
-
38
- def gem_list
39
- @gems
40
- end
41
- end
42
-
43
- protected
44
-
45
- def temp(file, data)
46
- tmp = Tempfile.new(file.gsub('/', '.'))
47
- tmp << data
48
- tmp.flush
49
- yield(tmp.path)
50
- tmp.close
51
- end
52
- end
53
- end
54
- end
@@ -1,173 +0,0 @@
1
- require 'dandelion/git'
2
-
3
- module Dandelion
4
- module Deployment
5
- class RemoteRevisionError < StandardError; end
6
- class FastForwardError < StandardError; end
7
-
8
- class Deployment
9
- class << self
10
- def create(repo, backend, options)
11
- begin
12
- DiffDeployment.new(repo, backend, options)
13
- rescue RemoteRevisionError
14
- FullDeployment.new(repo, backend, options)
15
- end
16
- end
17
- end
18
-
19
- def initialize(repo, backend, options = {})
20
- @repo = repo
21
- @backend = backend
22
- @options = { :exclude => [], :additional => [], :revision => 'HEAD', :revision_file => '.revision', :local_path => '' }.merge(options)
23
- @tree = Git::Tree.new(@repo, @options[:revision], @options[:local_path])
24
-
25
- if @options[:dry]
26
- # Stub out the destructive backend methods
27
- def @backend.write(file, data); end
28
- def @backend.delete(file); end
29
- end
30
- end
31
-
32
- def local_revision
33
- @tree.revision
34
- end
35
-
36
- def remote_revision
37
- nil
38
- end
39
-
40
- def write_revision
41
- @backend.write(@options[:revision_file], local_revision)
42
- end
43
-
44
- def validate
45
- begin
46
- raise FastForwardError if fast_forwardable
47
- rescue Grit::Git::CommandFailed
48
- end
49
- end
50
-
51
- def log
52
- Dandelion.logger
53
- end
54
-
55
- def deploy_additional
56
- if @options[:additional].nil? || @options[:additional].empty?
57
- log.debug("No additional files to deploy")
58
- return
59
- end
60
-
61
- @options[:additional].each do |file|
62
- local_path = file
63
- remote_path = file
64
-
65
- if file.is_a?(Hash)
66
- local_path, remote_path = file.first
67
- end
68
-
69
- log.debug("Uploading additional file: #{local_path} -> #{remote_path}")
70
- @backend.write(remote_path, IO.read(local_path))
71
- end
72
- end
73
-
74
- protected
75
-
76
- def exclude_file?(file)
77
- @options[:exclude].map { |e| file.start_with?(e) }.any? unless @options[:exclude].nil?
78
- end
79
-
80
- private
81
-
82
- def fast_forwardable
83
- !@repo.git.native(:cherry, {:raise => true, :timeout => false}).empty?
84
- end
85
- end
86
-
87
- class DiffDeployment < Deployment
88
- def initialize(repo, backend, options = {})
89
- super(repo, backend, options)
90
- @diff = Git::Diff.new(@repo, read_remote_revision, @options[:revision], @options[:local_path])
91
- end
92
-
93
- def remote_revision
94
- @diff.from_revision
95
- end
96
-
97
- def deploy
98
- if !revisions_match? && any?
99
- deploy_changed
100
- deploy_deleted
101
- else
102
- log.debug("No changes to deploy")
103
- end
104
-
105
- deploy_additional
106
-
107
- unless revisions_match?
108
- write_revision
109
- end
110
- end
111
-
112
- def deploy_changed
113
- @diff.changed.each do |file|
114
- if exclude_file?(file)
115
- log.debug("Skipping file: #{file}")
116
- else
117
- if data = @tree.show(file)
118
- log.debug("Uploading file: #{file}")
119
- @backend.write(file, data)
120
- end
121
- end
122
- end
123
- end
124
-
125
- def deploy_deleted
126
- @diff.deleted.each do |file|
127
- if exclude_file?(file)
128
- log.debug("Skipping file: #{file}")
129
- else
130
- log.debug("Deleting file: #{file}")
131
- @backend.delete(file)
132
- end
133
- end
134
- end
135
-
136
- def any?
137
- @diff.changed.any? || @diff.deleted.any?
138
- end
139
-
140
- def revisions_match?
141
- remote_revision == local_revision
142
- end
143
-
144
- private
145
-
146
- def read_remote_revision
147
- begin
148
- @backend.read(@options[:revision_file]).chomp
149
- rescue Backend::MissingFileError
150
- raise RemoteRevisionError
151
- end
152
- end
153
- end
154
-
155
- class FullDeployment < Deployment
156
- def deploy
157
- @tree.files.each do |file|
158
- if exclude_file?(file)
159
- log.debug("Skipping file: #{file}")
160
- else
161
- if data = @tree.show(file)
162
- log.debug("Uploading file: #{file}")
163
- @backend.write(file, data)
164
- end
165
- end
166
- end
167
-
168
- deploy_additional
169
- write_revision
170
- end
171
- end
172
- end
173
- end
data/lib/dandelion/git.rb DELETED
@@ -1,123 +0,0 @@
1
- require 'grit'
2
-
3
- module Dandelion
4
- module Git
5
- class DiffError < StandardError; end
6
- class RevisionError < StandardError; end
7
-
8
- class Repo < Grit::Repo
9
- def initialize(dir)
10
- super(dir)
11
- end
12
- end
13
-
14
- class Diff
15
- attr_reader :from_revision, :to_revision
16
-
17
- @files = nil
18
-
19
- def initialize(repo, from_revision, to_revision, local_path)
20
- @repo = repo
21
- @local_path = local_path
22
- @from_revision = revision_string(from_revision)
23
- @to_revision = revision_string(to_revision)
24
-
25
- begin
26
- @files = parse(diff)
27
- rescue Grit::Git::CommandFailed
28
- raise DiffError
29
- end
30
- end
31
-
32
- def changed
33
- @files.to_a.select { |f| ['A', 'C', 'M'].include?(f.last) }.map { |f| f.first }
34
- end
35
-
36
- def deleted
37
- @files.to_a.select { |f| 'D' == f.last }.map { |f| f.first }
38
- end
39
-
40
- private
41
-
42
- def diff
43
- @repo.git.native(:diff, { :name_status => true, :raise => true }, @from_revision, @to_revision)
44
- end
45
-
46
- def parse(diff)
47
- files = {}
48
- diff.split("\n").each do |line|
49
- status, file = line.split("\t")
50
- files[file] = status
51
- end
52
- files
53
- end
54
-
55
- def revision_string(revision)
56
- if @local_path.nil? || @local_path.empty?
57
- revision
58
- else
59
- "#{revision}:#{@local_path}"
60
- end
61
- end
62
- end
63
-
64
- class Tree
65
- def initialize(repo, revision, local_path)
66
- @repo = repo
67
- @commit = @repo.commit(revision)
68
- @revision = revision
69
- @local_path = local_path
70
- raise RevisionError if @commit.nil?
71
- @tree = @commit.tree
72
- end
73
-
74
- def files
75
- @repo.git.native(:ls_tree, { :name_only => true, :full_tree => true, :r => true }, revision_string).split("\n")
76
- end
77
-
78
- def show(file)
79
- blob = @tree / file_path(file)
80
- if blob.is_a?(Grit::Submodule)
81
- puts "#{file} is a submodule, ignoring."
82
- else
83
- blob.data
84
- end
85
- end
86
-
87
- def revision
88
- @commit.sha
89
- end
90
-
91
- private
92
-
93
- def file_path(file)
94
- if local_path?
95
- File.join(@local_path, file)
96
- else
97
- file
98
- end
99
- end
100
-
101
- def revision_string
102
- if local_path?
103
- "#{@revision}:#{@local_path}"
104
- else
105
- @revision
106
- end
107
- end
108
-
109
- def local_path?
110
- !@local_path.nil? && !@local_path.empty?
111
- end
112
- end
113
- end
114
- end
115
-
116
- # Grit does not support Ruby 2.0 right now
117
- class String
118
- if ((defined? RUBY_VERSION) && (RUBY_VERSION[0..2] == "1.9" || RUBY_VERSION[0].to_i >= 2))
119
- def getord(offset); self[offset].ord; end
120
- else
121
- alias :getord :[]
122
- end
123
- end
data/test/fixtures/diff DELETED
@@ -1,3 +0,0 @@
1
- M baz/foo
2
- A foo
3
- D foobar
@@ -1,5 +0,0 @@
1
- bar
2
- baz/bar
3
- baz/foo
4
- baz/foo/bar
5
- foo
@@ -1,122 +0,0 @@
1
- require 'dandelion'
2
- require 'dandelion/deployment'
3
- require 'test/unit'
4
-
5
- def fixture(name)
6
- File.read(File.join(File.dirname(__FILE__), 'fixtures', name))
7
- end
8
-
9
- # Mock classes
10
-
11
- class MockGit
12
- def native(cmd, options = {}, *args, &block)
13
- if cmd == :ls_tree
14
- fixture('ls_tree')
15
- elsif cmd == :diff
16
- fixture('diff')
17
- end
18
- end
19
- end
20
-
21
- class MockFile
22
- attr_reader :data
23
-
24
- def initialize(data)
25
- @data = data
26
- end
27
- end
28
-
29
- class MockTree
30
- def /(file)
31
- MockFile.new('bar')
32
- end
33
- end
34
-
35
- class MockCommit
36
- def initialize(revision)
37
- @revision = revision
38
- end
39
-
40
- def tree
41
- MockTree.new
42
- end
43
-
44
- def sha
45
- @revision
46
- end
47
- end
48
-
49
- class MockRepo
50
- def commit(revision)
51
- MockCommit.new(revision)
52
- end
53
-
54
- def git
55
- MockGit.new
56
- end
57
- end
58
-
59
- class MockBackend
60
- attr_reader :reads, :writes, :deletes
61
-
62
- def initialize(remote_revision)
63
- @reads = {'.revision' => remote_revision}
64
- @writes = {}
65
- @deletes = []
66
- end
67
-
68
- def read(file)
69
- @reads[file]
70
- end
71
-
72
- def write(file, data)
73
- @writes[file] = data
74
- end
75
-
76
- def delete(file)
77
- @deletes << file
78
- end
79
- end
80
-
81
- # Tests
82
-
83
- class TestDiffDeployment < Test::Unit::TestCase
84
- def setup
85
- Dandelion.logger.level = Logger::FATAL
86
- @head_revision = '0ca605e9f0f1d42ce8193ac36db11ec3cc9efc08'
87
- @remote_revision = 'ff1f1d4bd0c99e1c9cca047c46b2194accf89504'
88
- @repo = MockRepo.new
89
- @backend = MockBackend.new(@remote_revision)
90
- @diff_deployment = Dandelion::Deployment::DiffDeployment.new(@repo, @backend, :revision => @head_revision)
91
- end
92
-
93
- def test_diff_deployment_local_revision
94
- assert_equal @head_revision, @diff_deployment.local_revision
95
- end
96
-
97
- def test_diff_deployment_remote_revision
98
- assert_equal @remote_revision, @diff_deployment.remote_revision
99
- end
100
-
101
- def test_diff_deployment_write_revision
102
- @diff_deployment.write_revision
103
- assert_equal @head_revision, @backend.writes['.revision']
104
- end
105
-
106
- def test_diff_deployment_revisions_match
107
- assert !@diff_deployment.revisions_match?
108
- end
109
-
110
- def test_diff_deployment_any
111
- assert @diff_deployment.any?
112
- end
113
-
114
- def test_diff_deployment_deploy
115
- @diff_deployment.deploy
116
- assert_equal 3, @backend.writes.length
117
- assert_equal 'bar', @backend.writes['foo']
118
- assert_equal 'bar', @backend.writes['baz/foo']
119
- assert_equal @head_revision, @backend.writes['.revision']
120
- assert_equal ['foobar'], @backend.deletes
121
- end
122
- end