dandelion 0.3.15 → 0.4.0.beta2

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.
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 --