dandelion 0.3.11 → 0.3.12

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .DS_Store
data/README.md CHANGED
@@ -7,30 +7,44 @@ Install
7
7
  Ensure that Ruby and RubyGems are installed, then run:
8
8
 
9
9
  $ gem install dandelion
10
-
10
+
11
11
  Alternatively, you can build the gem yourself:
12
12
 
13
13
  $ git clone git://github.com/scttnlsn/dandelion.git
14
14
  $ cd dandelion
15
15
  $ rake install
16
-
16
+
17
17
  Config
18
18
  ------
19
19
  Configuration options are specified in a YAML file (by default, the root of your
20
20
  Git repository is searched for a file named `dandelion.yml`). Example:
21
21
 
22
22
  # Required
23
+ # --------
24
+
23
25
  scheme: sftp
24
26
  host: example.com
25
27
  username: user
26
28
  password: pass
27
-
29
+
28
30
  # Optional
31
+ # --------
32
+
33
+ # Remote path
29
34
  path: path/to/deployment
35
+
36
+ # Local Path
37
+ local_path: path/in/repo
38
+
39
+ # Remote file name in which the current revision is stored
40
+ revision_file: .revision
41
+
42
+ # These files (from Git) will not be uploaded during a deploy
30
43
  exclude:
31
44
  - .gitignore
32
45
  - dandelion.yml
33
- revision_file: .revision
46
+
47
+ # These files (from your working directory) will be uploaded on every deploy
34
48
  additional:
35
49
  - public/css/print.css
36
50
  - public/css/screen.css
@@ -53,7 +67,9 @@ Required:
53
67
  Optional:
54
68
 
55
69
  * `path`
56
- * `exclude`
70
+ * `local_path` (defaults to repository root)
71
+ * `exclude` (if local_path is set, files are relative to that path)
72
+ * `additional`
57
73
  * `port`
58
74
  * `revision_file` (defaults to .revision)
59
75
  * `preserve_permissions` (defaults to true)
@@ -69,11 +85,13 @@ Required:
69
85
  Optional:
70
86
 
71
87
  * `path`
72
- * `exclude`
88
+ * `local_path` (defaults to repository root)
89
+ * `exclude` (if local_path is set, files are relative to that path)
90
+ * `additional`
73
91
  * `port`
74
92
  * `revision_file` (defaults to .revision)
75
93
  * `passive` (defaults to true)
76
-
94
+
77
95
  **Amazon S3**: `scheme: s3`
78
96
 
79
97
  Required:
@@ -85,7 +103,9 @@ Required:
85
103
  Optional:
86
104
 
87
105
  * `path`
88
- * `exclude`
106
+ * `local_path` (defaults to repository root)
107
+ * `exclude` (if local_path is set, files are relative to that path)
108
+ * `additional`
89
109
  * `revision_file` (defaults to .revision)
90
110
 
91
111
  Usage
@@ -93,7 +113,7 @@ Usage
93
113
  From within your Git repository, run:
94
114
 
95
115
  $ dandelion deploy
96
-
116
+
97
117
  This will deploy the local `HEAD` revision to the location specified in the config
98
118
  file. Dandelion keeps track of the currently deployed revision so that only files
99
119
  which have been added/changed/deleted need to be transferred.
@@ -115,7 +135,7 @@ For a more complete summary of usage options, run:
115
135
  Available commands:
116
136
  deploy
117
137
  status
118
-
138
+
119
139
  Note that when specifying the repository or configuration file, the given paths
120
140
  are relative to the current working directory (not the repository root). To see
121
141
  the options for a particular command, run:
@@ -9,7 +9,7 @@ require 'yaml'
9
9
  module Dandelion
10
10
  module Command
11
11
  class InvalidCommandError < StandardError; end
12
-
12
+
13
13
  class Base
14
14
  class << self
15
15
  @@commands = {}
@@ -23,11 +23,11 @@ module Dandelion
23
23
  raise InvalidCommandError unless @@commands.include?(name)
24
24
  @@commands[name]
25
25
  end
26
-
26
+
27
27
  def commands
28
28
  @@commands.keys
29
29
  end
30
-
30
+
31
31
  def require_commands
32
32
  Dir.glob(File.join(File.dirname(__FILE__), 'command', '*.rb')) { |file| require file }
33
33
  end
@@ -65,16 +65,16 @@ module Dandelion
65
65
  opts.on('--repo=[REPO]', 'Use the given repository') do |repo|
66
66
  options[:repo] = File.expand_path(repo)
67
67
  end
68
-
68
+
69
69
  options[:config] = nil
70
70
  opts.on('--config=[CONFIG]', 'Use the given configuration file') do |config|
71
71
  options[:config] = File.expand_path(config)
72
72
  end
73
73
  end
74
74
  end
75
-
75
+
76
76
  private
77
-
77
+
78
78
  def closest_repo(dir)
79
79
  if File.exists?(File.join(dir, '.git'))
80
80
  dir
@@ -85,15 +85,15 @@ module Dandelion
85
85
  end
86
86
 
87
87
  def initialize(options)
88
- @options = options
88
+ @options = options
89
89
  @config = YAML.load_file(@options[:config])
90
90
  @repo = Git::Repo.new(@options[:repo])
91
-
91
+
92
92
  yield(self) if block_given?
93
93
  end
94
-
94
+
95
95
  protected
96
-
96
+
97
97
  def log
98
98
  Dandelion.logger
99
99
  end
@@ -113,17 +113,19 @@ module Dandelion
113
113
  exit 1
114
114
  end
115
115
  end
116
-
116
+
117
117
  def deployment(revision, backend = nil)
118
118
  begin
119
119
  backend ||= backend()
120
120
  revision_file = @config['revision_file'].nil? ? '.revision' : @config['revision_file']
121
+ local_path = @config['local_path'].nil? ? '' : @config['local_path']
121
122
  options = {
122
123
  :dry => @options[:dry],
123
124
  :exclude => @config['exclude'],
124
125
  :additional => @config['additional'],
125
126
  :revision => revision,
126
- :revision_file => revision_file
127
+ :revision_file => revision_file,
128
+ :local_path => local_path
127
129
  }
128
130
 
129
131
  Deployment::Deployment.create(@repo, backend, options)
@@ -4,7 +4,7 @@ module Dandelion
4
4
  module Deployment
5
5
  class RemoteRevisionError < StandardError; end
6
6
  class FastForwardError < StandardError; end
7
-
7
+
8
8
  class Deployment
9
9
  class << self
10
10
  def create(repo, backend, options)
@@ -15,39 +15,39 @@ module Dandelion
15
15
  end
16
16
  end
17
17
  end
18
-
18
+
19
19
  def initialize(repo, backend, options = {})
20
20
  @repo = repo
21
21
  @backend = backend
22
- @options = { :exclude => [], :additional => [], :revision => 'HEAD', :revision_file => '.revision' }.merge(options)
23
- @tree = Git::Tree.new(@repo, @options[:revision])
24
-
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
25
  if @options[:dry]
26
26
  # Stub out the destructive backend methods
27
27
  def @backend.write(file, data); end
28
28
  def @backend.delete(file); end
29
29
  end
30
30
  end
31
-
31
+
32
32
  def local_revision
33
33
  @tree.revision
34
34
  end
35
-
35
+
36
36
  def remote_revision
37
37
  nil
38
38
  end
39
-
39
+
40
40
  def write_revision
41
41
  @backend.write(@options[:revision_file], local_revision)
42
42
  end
43
-
43
+
44
44
  def validate
45
45
  begin
46
46
  raise FastForwardError if fast_forwardable
47
47
  rescue Grit::Git::CommandFailed
48
48
  end
49
49
  end
50
-
50
+
51
51
  def log
52
52
  Dandelion.logger
53
53
  end
@@ -63,30 +63,30 @@ module Dandelion
63
63
  @backend.write(file, IO.read(file))
64
64
  end
65
65
  end
66
-
66
+
67
67
  protected
68
-
68
+
69
69
  def exclude_file?(file)
70
70
  @options[:exclude].map { |e| file.start_with?(e) }.any? unless @options[:exclude].nil?
71
71
  end
72
-
72
+
73
73
  private
74
-
74
+
75
75
  def fast_forwardable
76
76
  !@repo.git.native(:cherry, {:raise => true, :timeout => false}).empty?
77
77
  end
78
78
  end
79
-
79
+
80
80
  class DiffDeployment < Deployment
81
81
  def initialize(repo, backend, options = {})
82
82
  super(repo, backend, options)
83
- @diff = Git::Diff.new(@repo, read_remote_revision, @options[:revision])
83
+ @diff = Git::Diff.new(@repo, read_remote_revision, @options[:revision], @options[:local_path])
84
84
  end
85
-
85
+
86
86
  def remote_revision
87
87
  @diff.from_revision
88
88
  end
89
-
89
+
90
90
  def deploy
91
91
  if !revisions_match? && any?
92
92
  deploy_changed
@@ -107,12 +107,14 @@ module Dandelion
107
107
  if exclude_file?(file)
108
108
  log.debug("Skipping file: #{file}")
109
109
  else
110
- log.debug("Uploading file: #{file}")
111
- @backend.write(file, @tree.show(file))
110
+ if data = @tree.show(file)
111
+ log.debug("Uploading file: #{file}")
112
+ @backend.write(file, data)
113
+ end
112
114
  end
113
115
  end
114
116
  end
115
-
117
+
116
118
  def deploy_deleted
117
119
  @diff.deleted.each do |file|
118
120
  if exclude_file?(file)
@@ -123,17 +125,17 @@ module Dandelion
123
125
  end
124
126
  end
125
127
  end
126
-
128
+
127
129
  def any?
128
130
  @diff.changed.any? || @diff.deleted.any?
129
131
  end
130
-
132
+
131
133
  def revisions_match?
132
134
  remote_revision == local_revision
133
135
  end
134
-
136
+
135
137
  private
136
-
138
+
137
139
  def read_remote_revision
138
140
  begin
139
141
  @backend.read(@options[:revision_file]).chomp
@@ -142,18 +144,20 @@ module Dandelion
142
144
  end
143
145
  end
144
146
  end
145
-
147
+
146
148
  class FullDeployment < Deployment
147
149
  def deploy
148
150
  @tree.files.each do |file|
149
151
  if exclude_file?(file)
150
152
  log.debug("Skipping file: #{file}")
151
153
  else
152
- log.debug("Uploading file: #{file}")
153
- @backend.write(file, @tree.show(file))
154
+ if data = @tree.show(file)
155
+ log.debug("Uploading file: #{file}")
156
+ @backend.write(file, data)
157
+ end
154
158
  end
155
159
  end
156
-
160
+
157
161
  deploy_additional
158
162
  write_revision
159
163
  end
data/lib/dandelion/git.rb CHANGED
@@ -4,22 +4,27 @@ module Dandelion
4
4
  module Git
5
5
  class DiffError < StandardError; end
6
6
  class RevisionError < StandardError; end
7
-
7
+
8
8
  class Repo < Grit::Repo
9
9
  def initialize(dir)
10
10
  super(dir)
11
11
  end
12
12
  end
13
-
13
+
14
14
  class Diff
15
15
  attr_reader :from_revision, :to_revision
16
-
16
+
17
17
  @files = nil
18
-
19
- def initialize(repo, from_revision, to_revision)
18
+
19
+ def initialize(repo, from_revision, to_revision, local_path)
20
20
  @repo = repo
21
+ @local_path = local_path
21
22
  @from_revision = from_revision
22
23
  @to_revision = to_revision
24
+ unless @local_path.nil? || @local_path.empty?
25
+ @from_revision = "#{@from_revision}:#{@local_path}"
26
+ @to_revision = "#{@to_revision}:#{@local_path}"
27
+ end
23
28
  begin
24
29
  @files = parse(diff)
25
30
  rescue Grit::Git::CommandFailed
@@ -36,11 +41,11 @@ module Dandelion
36
41
  end
37
42
 
38
43
  private
39
-
44
+
40
45
  def diff
41
- @repo.git.native(:diff, {:name_status => true, :raise => true}, from_revision, to_revision)
46
+ @repo.git.native(:diff, {:name_status => true, :raise => true}, @from_revision, @to_revision)
42
47
  end
43
-
48
+
44
49
  def parse(diff)
45
50
  files = {}
46
51
  diff.split("\n").each do |line|
@@ -52,24 +57,33 @@ module Dandelion
52
57
  end
53
58
 
54
59
  class Tree
55
- def initialize(repo, revision)
60
+ def initialize(repo, revision, local_path)
56
61
  @repo = repo
57
62
  @commit = @repo.commit(revision)
63
+ @revision = revision
64
+ @local_path = local_path
58
65
  raise RevisionError if @commit.nil?
59
66
  @tree = @commit.tree
60
67
  end
61
-
68
+
62
69
  def files
63
- @repo.git.native(:ls_tree, {:name_only => true, :r => true}, revision).split("\n")
70
+ @revision = "#{@revision}:#{@local_path}" unless @local_path.nil? || @local_path.empty?
71
+ @repo.git.native(:ls_tree, {:name_only => true, :full_tree => true, :r => true}, @revision).split("\n")
64
72
  end
65
73
 
66
74
  def show(file)
67
- (@tree / file).data
75
+ @file = file
76
+ @file = "#{@local_path}/#{file}" unless @local_path.nil? || @local_path.empty?
77
+ if (@tree / "#{@file}").is_a?(Grit::Submodule)
78
+ puts "#{file} is a submodule, ignoring."
79
+ return
80
+ end
81
+ (@tree / "#{@file}").data
68
82
  end
69
-
83
+
70
84
  def revision
71
85
  @commit.sha
72
86
  end
73
87
  end
74
88
  end
75
- end
89
+ end
@@ -1,3 +1,3 @@
1
1
  module Dandelion
2
- VERSION = '0.3.11'
2
+ VERSION = '0.3.12'
3
3
  end
@@ -1,4 +1,5 @@
1
1
  bar
2
2
  baz/bar
3
3
  baz/foo
4
- foo
4
+ baz/foo/bar
5
+ foo
@@ -20,7 +20,7 @@ end
20
20
 
21
21
  class MockFile
22
22
  attr_reader :data
23
-
23
+
24
24
  def initialize(data)
25
25
  @data = data
26
26
  end
@@ -36,11 +36,11 @@ class MockCommit
36
36
  def initialize(revision)
37
37
  @revision = revision
38
38
  end
39
-
39
+
40
40
  def tree
41
41
  MockTree.new
42
42
  end
43
-
43
+
44
44
  def sha
45
45
  @revision
46
46
  end
@@ -50,7 +50,7 @@ class MockRepo
50
50
  def commit(revision)
51
51
  MockCommit.new(revision)
52
52
  end
53
-
53
+
54
54
  def git
55
55
  MockGit.new
56
56
  end
@@ -58,21 +58,21 @@ end
58
58
 
59
59
  class MockBackend
60
60
  attr_reader :reads, :writes, :deletes
61
-
61
+
62
62
  def initialize(remote_revision)
63
63
  @reads = {'.revision' => remote_revision}
64
64
  @writes = {}
65
65
  @deletes = []
66
66
  end
67
-
67
+
68
68
  def read(file)
69
69
  @reads[file]
70
70
  end
71
-
71
+
72
72
  def write(file, data)
73
73
  @writes[file] = data
74
74
  end
75
-
75
+
76
76
  def delete(file)
77
77
  @deletes << file
78
78
  end
@@ -89,28 +89,28 @@ class TestDiffDeployment < Test::Unit::TestCase
89
89
  @backend = MockBackend.new(@remote_revision)
90
90
  @diff_deployment = Dandelion::Deployment::DiffDeployment.new(@repo, @backend, :revision => @head_revision)
91
91
  end
92
-
92
+
93
93
  def test_diff_deployment_local_revision
94
94
  assert_equal @head_revision, @diff_deployment.local_revision
95
95
  end
96
-
96
+
97
97
  def test_diff_deployment_remote_revision
98
98
  assert_equal @remote_revision, @diff_deployment.remote_revision
99
99
  end
100
-
100
+
101
101
  def test_diff_deployment_write_revision
102
102
  @diff_deployment.write_revision
103
103
  assert_equal @head_revision, @backend.writes['.revision']
104
104
  end
105
-
105
+
106
106
  def test_diff_deployment_revisions_match
107
107
  assert !@diff_deployment.revisions_match?
108
108
  end
109
-
109
+
110
110
  def test_diff_deployment_any
111
111
  assert @diff_deployment.any?
112
112
  end
113
-
113
+
114
114
  def test_diff_deployment_deploy
115
115
  @diff_deployment.deploy
116
116
  assert_equal 3, @backend.writes.length
data/test/test_git.rb CHANGED
@@ -5,40 +5,46 @@ class TestGit < Test::Unit::TestCase
5
5
  def setup
6
6
  @repo = Dandelion::Git::Repo.new(File.join(File.dirname(__FILE__), 'test_git.git'))
7
7
  end
8
-
8
+
9
9
  def test_tree_files
10
- tree = Dandelion::Git::Tree.new(@repo, 'HEAD')
10
+ tree = Dandelion::Git::Tree.new(@repo, 'HEAD', '')
11
11
  files = ['foo', 'bar', 'baz/foo', 'baz/bar']
12
12
  assert_equal files.sort, tree.files.sort
13
13
  end
14
-
14
+
15
+ def test_tree_subfolder
16
+ tree = Dandelion::Git::Tree.new(@repo, 'HEAD', 'baz')
17
+ files = ['foo', 'bar']
18
+ assert_equal files.sort, tree.files.sort
19
+ end
20
+
15
21
  def test_tree_show
16
- tree = Dandelion::Git::Tree.new(@repo, 'HEAD')
22
+ tree = Dandelion::Git::Tree.new(@repo, 'HEAD', '')
17
23
  assert_equal "bar\n", tree.show('foo')
18
24
  assert_equal "bar\n", tree.show('baz/foo')
19
25
  end
20
-
26
+
21
27
  def test_tree_revision
22
28
  revision = 'ff1f1d4bd0c99e1c9cca047c46b2194accf89504'
23
- tree = Dandelion::Git::Tree.new(@repo, revision)
29
+ tree = Dandelion::Git::Tree.new(@repo, revision, '')
24
30
  assert_equal revision, tree.revision
25
31
  end
26
-
32
+
27
33
  def test_diff_changed
28
34
  from = 'ff1f1d4bd0c99e1c9cca047c46b2194accf89504'
29
35
  to = '88d4480861346093048e08ce8dcc577d8aa69379'
30
36
  files = ['foo', 'baz/foo']
31
- diff = Dandelion::Git::Diff.new(@repo, from, to)
37
+ diff = Dandelion::Git::Diff.new(@repo, from, to, '')
32
38
  assert_equal files.sort, diff.changed.sort
33
39
  assert_equal [], diff.deleted
34
40
  end
35
-
41
+
36
42
  def test_diff_deleted
37
43
  from = 'f55f3c44c89e5d215fbaaef9d33563117fe0b61b'
38
44
  to = '0ca605e9f0f1d42ce8193ac36db11ec3cc9efc08'
39
45
  files = ['test_delete']
40
- diff = Dandelion::Git::Diff.new(@repo, from, to)
46
+ diff = Dandelion::Git::Diff.new(@repo, from, to, '')
41
47
  assert_equal files.sort, diff.deleted.sort
42
48
  assert_equal [], diff.changed
43
49
  end
44
- end
50
+ end
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: dandelion
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.11
5
+ version: 0.3.12
6
6
  platform: ruby
7
7
  authors:
8
8
  - Scott Nelson
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-02 00:00:00.000000000 Z
12
+ date: 2013-07-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  version_requirements: !ruby/object:Gem::Requirement
@@ -157,7 +157,7 @@ rubyforge_project:
157
157
  rubygems_version: 1.8.23
158
158
  signing_key:
159
159
  specification_version: 3
160
- summary: dandelion-0.3.11
160
+ summary: dandelion-0.3.12
161
161
  test_files:
162
162
  - test/fixtures/diff
163
163
  - test/fixtures/ls_tree