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
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Command::Deploy do
4
+ let(:config) {{}}
5
+ let(:options) {{}}
6
+
7
+ let(:adapter) { double('adapter') }
8
+ let(:workspace) { Dandelion::Workspace.new(test_repo, adapter) }
9
+ let(:command) { Dandelion::Command::Deploy.new(workspace, config, options) }
10
+
11
+ describe '#parser' do
12
+ let(:options) { {} }
13
+ let(:parser) { Dandelion::Command::Deploy.parser(options) }
14
+
15
+ it 'parses dry flag' do
16
+ expect(options[:dry]).to eq nil
17
+ parser.order!(['--dry-run'])
18
+ expect(options[:dry]).to eq true
19
+ end
20
+ end
21
+
22
+ describe '#deployer_adapter' do
23
+ it 'returns workspace adapter' do
24
+ adapter = double('adapter')
25
+ workspace.should_receive(:adapter).and_return(adapter)
26
+ expect(command.deployer_adapter).to eq adapter
27
+ end
28
+
29
+ context 'dry run' do
30
+ before(:each) { options[:dry] = true }
31
+
32
+ it 'uses no-op adapter' do
33
+ noop = double('no-op adapter')
34
+ Dandelion::Adapter::NoOpAdapter.should_receive(:new).with(command.config).and_return(noop)
35
+ expect(command.deployer_adapter).to eq noop
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#deployer' do
41
+ before(:each) { command.stub(:adapter).and_return(double('adapter')) }
42
+
43
+ it 'creates deployer for adapter, and config' do
44
+ deployer = double('deployer')
45
+ Dandelion::Deployer.should_receive(:new).with(command.deployer_adapter, command.config).and_return(deployer)
46
+ expect(command.deployer).to eq deployer
47
+ end
48
+ end
49
+
50
+ describe '#setup' do
51
+ it 'sets revision' do
52
+ command.setup([:foo])
53
+ expect(command.config[:revision]).to eq :foo
54
+ end
55
+ end
56
+
57
+ describe '#execute!' do
58
+ let(:adapter) { Dandelion::Adapter::NoOpAdapter.new(config) }
59
+ let(:workspace) { Dandelion::Workspace.new(test_repo, adapter) }
60
+ let(:command) { Dandelion::Command::Deploy.new(workspace, config, options) }
61
+
62
+ let(:deployer) { double('deployer') }
63
+ let(:changeset) { double('changeset') }
64
+
65
+ before(:each) do
66
+ workspace.stub(:changeset).and_return(changeset)
67
+ command.stub(:deployer).and_return(deployer)
68
+ end
69
+
70
+ context 'empty changeset' do
71
+ before(:each) { changeset.stub(:empty?).and_return(true) }
72
+
73
+ it 'does nothing' do
74
+ deployer.should_not_receive(:deploy!)
75
+ end
76
+ end
77
+
78
+ context 'non-empty changeset' do
79
+ before(:each) do
80
+ deployer.stub(:deploy_changeset!)
81
+ changeset.stub(:empty?).and_return(false)
82
+ end
83
+
84
+ it 'deploys changeset' do
85
+ deployer.should_receive(:deploy_changeset!).with(changeset)
86
+ command.execute!
87
+ end
88
+
89
+ it 'sets remote revision to local revision' do
90
+ workspace.should_receive(:remote_commit=).with(workspace.local_commit)
91
+ command.execute!
92
+ end
93
+ end
94
+
95
+ context 'non-empty additional files' do
96
+ before(:each) do
97
+ changeset.stub(:empty?).and_return(true)
98
+ end
99
+
100
+ before(:each) do
101
+ deployer.stub(:deploy_files!)
102
+ config[:additional] = ['foo']
103
+ end
104
+
105
+ it 'deploys files' do
106
+ deployer.should_receive(:deploy_files!).with(['foo'])
107
+ command.execute!
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Command::Status do
4
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Command::Base do
4
+ let(:workspace) { double('workspace', adapter: double('adapter')) }
5
+ let(:config) { double('config') }
6
+ let(:options) { double('options') }
7
+
8
+ let(:command) { Dandelion::Command::Base.new(workspace, config, options) }
9
+
10
+ it 'registers command classes' do
11
+ class TestCommand < Dandelion::Command::Base
12
+ command :test
13
+ end
14
+
15
+ expect(Dandelion::Command::Base.lookup(:test)).to eq TestCommand
16
+ end
17
+
18
+ it 'raises error on invalid command' do
19
+ expect {
20
+ Dandelion::Command::Base.lookup(:another)
21
+ }.to raise_error(Dandelion::Command::InvalidCommandError)
22
+ end
23
+
24
+ it 'has workspace' do
25
+ expect(command.workspace).to eq workspace
26
+ end
27
+
28
+ it 'has config' do
29
+ expect(command.config).to eq config
30
+ end
31
+
32
+ it 'has options' do
33
+ expect(command.options).to eq options
34
+ end
35
+
36
+ describe '#adapter' do
37
+ it 'returns workspace adapter' do
38
+ expect(command.adapter).to eq workspace.adapter
39
+ end
40
+ end
41
+
42
+ describe '#parser' do
43
+ let(:options) { {} }
44
+ let(:parser) { Dandelion::Command::Base.parser(options) }
45
+
46
+ it 'parses version flag' do
47
+ expect(options[:version]).to_not be
48
+ parser.order!(['-v'])
49
+ expect(options[:version]).to be
50
+ end
51
+
52
+ it 'parses help flag' do
53
+ expect(options[:help]).to_not be
54
+ parser.order!(['-h'])
55
+ expect(options[:help]).to be
56
+ end
57
+
58
+ it 'parses repo option' do
59
+ expect(options[:repo]).to eq nil
60
+ parser.order!(['--repo=foo'])
61
+ expect(options[:repo]).to eq 'foo'
62
+ end
63
+
64
+ it 'parses config option' do
65
+ expect(options[:config]).to eq nil
66
+ parser.order!(['--config=foo'])
67
+ expect(options[:config]).to eq 'foo'
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Config do
4
+ let(:data) {{ 'foo' => 'bar' }}
5
+
6
+ before(:each) do
7
+ YAML.should_receive(:load_file).with('foo').and_return(data)
8
+ end
9
+
10
+ let(:config) { Dandelion::Config.new(path: 'foo') }
11
+
12
+ it 'parses yaml config file' do
13
+ expect(config[:foo]).to eq 'bar'
14
+ end
15
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Deployer do
4
+ let(:adapter) { double('adapter') }
5
+ let(:deployer) { Dandelion::Deployer.new(adapter) }
6
+
7
+ describe '#deploy_changeset!' do
8
+ let(:changeset) {[
9
+ double(path: 'foo', data: 'bar', type: :write),
10
+ double(path: 'bar/baz', data: 'baz', type: :write),
11
+ double(path: 'qux', type: :delete)
12
+ ]}
13
+
14
+ it 'perfoms writes and deletions on adapter' do
15
+ adapter.should_receive(:write).with('foo', 'bar')
16
+ adapter.should_receive(:write).with('bar/baz', 'baz')
17
+ adapter.should_receive(:delete).with('qux')
18
+
19
+ deployer.deploy_changeset!(changeset)
20
+ end
21
+
22
+ context 'excluded' do
23
+ let(:deployer) { Dandelion::Deployer.new(adapter, exclude: ['foo']) }
24
+
25
+ it 'perfoms writes and deletions on adapter' do
26
+ adapter.should_receive(:write).with('bar/baz', 'baz')
27
+ adapter.should_receive(:delete).with('qux')
28
+
29
+ deployer.deploy_changeset!(changeset)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#deploy_files!' do
35
+ before(:each) do
36
+ IO.stub(:read).with('a.txt').and_return('A')
37
+ IO.stub(:read).with('b.txt').and_return('B')
38
+ end
39
+
40
+ context 'local paths' do
41
+ let(:files) { ['a.txt', 'b.txt'] }
42
+
43
+ it 'performs writes on adapter' do
44
+ adapter.should_receive(:write).with('a.txt', 'A')
45
+ adapter.should_receive(:write).with('b.txt', 'B')
46
+
47
+ deployer.deploy_files!(files)
48
+ end
49
+ end
50
+
51
+ context 'local and remote paths' do
52
+ let(:files) {[
53
+ { 'a.txt' => 'files/a.txt' },
54
+ { 'b.txt' => 'files/b.txt' }
55
+ ]}
56
+
57
+ it 'performs writes on adapter' do
58
+ adapter.should_receive(:write).with('files/a.txt', 'A')
59
+ adapter.should_receive(:write).with('files/b.txt', 'B')
60
+
61
+ deployer.deploy_files!(files)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Diff do
4
+ let(:from_commit) { test_repo.lookup('e289ff1e2729839759dbd6fe99b6e35880910c7c') }
5
+ let(:to_commit) { test_repo.lookup('3d9b743acb4a84dd99002d2c6f3fcf1a47e9f06b') }
6
+
7
+ context 'non-nil from commit' do
8
+ let(:diff) { test_diff }
9
+
10
+ describe '#empty?' do
11
+ it 'returns true if there are no changes' do
12
+ expect(diff.empty?).to_not be
13
+ end
14
+ end
15
+
16
+ describe '#enumerable' do
17
+ it 'returns all changes between commits' do
18
+ expect(diff.to_a.length).to eq 5
19
+ end
20
+
21
+ it 'returns write paths' do
22
+ changes = diff.select { |c| c.type == :write }.map(&:path)
23
+
24
+ expect(changes).to include 'foo'
25
+ expect(changes).to include 'qux'
26
+ expect(changes).to include 'baz/bar'
27
+ expect(changes.length).to eq 3
28
+ end
29
+
30
+ it 'returns delete paths' do
31
+ changes = diff.select { |c| c.type == :delete }.map(&:path)
32
+
33
+ expect(changes).to include 'bar'
34
+ expect(changes).to include 'baz/foo'
35
+ expect(changes.length).to eq 2
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'nil from commit' do
41
+ let(:diff) { Dandelion::Diff.new(nil, to_commit) }
42
+
43
+ describe '#enumerable' do
44
+ it 'returns all paths in to commit' do
45
+ changes = diff.map(&:path)
46
+
47
+ expect(changes).to include 'foo'
48
+ expect(changes).to include 'qux'
49
+ expect(changes).to include 'baz/bar'
50
+ expect(changes.length).to eq 3
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Tree do
4
+ let(:tree) { test_tree }
5
+
6
+ it 'has a commit' do
7
+ expect(tree.commit).to eq test_commits.last
8
+ end
9
+
10
+ describe '#data' do
11
+ it 'returns blob content for path' do
12
+ expect(tree.data('foo')).to eq "foo\n"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dandelion::Workspace do
4
+ let(:adapter) { double('adapter') }
5
+ let(:workspace) { Dandelion::Workspace.new(test_repo, adapter) }
6
+
7
+ let(:head_revision) { '3d9b743acb4a84dd99002d2c6f3fcf1a47e9f06b' }
8
+ let(:initial_revision) { 'e289ff1e2729839759dbd6fe99b6e35880910c7c' }
9
+
10
+ it 'has an adapter' do
11
+ expect(workspace.adapter).to eq adapter
12
+ end
13
+
14
+ describe '#local_commit' do
15
+ context 'no revision specified' do
16
+ it 'returns head commit' do
17
+ expect(workspace.local_commit.oid).to eq head_revision
18
+ end
19
+ end
20
+
21
+ context 'valid revision specified' do
22
+ let(:workspace) { Dandelion::Workspace.new(test_repo, adapter, revision: initial_revision) }
23
+
24
+ it 'returns commit for given revision' do
25
+ expect(workspace.local_commit.oid).to eq initial_revision
26
+ end
27
+ end
28
+
29
+ context 'invalid revision specified' do
30
+ let(:workspace) { Dandelion::Workspace.new(test_repo, adapter, revision: 'abcdef' ) }
31
+
32
+ it 'raises revision error' do
33
+ expect { workspace.local_commit }.to raise_error(Dandelion::RevisionError)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#remote_commit' do
39
+ before(:each) do
40
+ adapter.stub(:read).with('.revision').and_return(initial_revision)
41
+ end
42
+
43
+ it 'returns commit for revision read from adapter' do
44
+ expect(workspace.remote_commit.oid).to eq initial_revision
45
+ end
46
+ end
47
+
48
+ describe '#remote_commit=' do
49
+ it 'writes commit revision to adapter' do
50
+ adapter.should_receive(:write).with('.revision', head_revision)
51
+ workspace.remote_commit = workspace.local_commit
52
+ end
53
+ end
54
+
55
+ describe '#tree' do
56
+ it 'returns tree for repo and local commit' do
57
+ tree = double()
58
+ Dandelion::Tree.should_receive(:new).with(test_repo, workspace.local_commit).and_return(tree)
59
+ expect(workspace.tree).to eq tree
60
+ end
61
+ end
62
+
63
+ describe '#changeset' do
64
+ it 'returns changeset for tree and remote commit' do
65
+ changeset = double('changeset')
66
+ tree = double('tree')
67
+ remote_commit = double('remote_commit')
68
+
69
+ workspace.stub(:tree).and_return(tree)
70
+ workspace.stub(:remote_commit).and_return(remote_commit)
71
+
72
+ Dandelion::Changeset.should_receive(:new).with(tree, remote_commit, workspace.config).and_return(changeset)
73
+
74
+ expect(workspace.changeset).to eq changeset
75
+ end
76
+ end
77
+ end
File without changes
@@ -3,3 +3,4 @@
3
3
  filemode = true
4
4
  bare = true
5
5
  ignorecase = true
6
+ precomposeunicode = false
@@ -18,6 +18,9 @@ fi
18
18
  # If you want to allow non-ascii filenames set this variable to true.
19
19
  allownonascii=$(git config hooks.allownonascii)
20
20
 
21
+ # Redirect output to stderr.
22
+ exec 1>&2
23
+
21
24
  # Cross platform projects tend to avoid non-ascii filenames; prevent
22
25
  # them from being added to the repository. We exploit the fact that the
23
26
  # printable range starts at the space character and ends with tilde.
@@ -25,8 +28,8 @@ if [ "$allownonascii" != "true" ] &&
25
28
  # Note that the use of brackets around a tr range is ok here, (it's
26
29
  # even required, for portability to Solaris 10's /usr/bin/tr), since
27
30
  # the square bracket bytes happen to fall in the designated range.
28
- test "$(git diff --cached --name-only --diff-filter=A -z $against |
29
- LC_ALL=C tr -d '[ -~]\0')"
31
+ test $(git diff --cached --name-only --diff-filter=A -z $against |
32
+ LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
30
33
  then
31
34
  echo "Error: Attempt to add a non-ascii file name."
32
35
  echo
@@ -43,4 +46,5 @@ then
43
46
  exit 1
44
47
  fi
45
48
 
49
+ # If there are whitespace errors, print the offending file names and fail.
46
50
  exec git diff-index --check --cached $against --