dandelion 0.2.3 → 0.3.0

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/README.md CHANGED
@@ -1,5 +1,6 @@
1
- dandelion
1
+ Dandelion
2
2
  =========
3
+ Incremental Git repository deployment.
3
4
 
4
5
  Install
5
6
  -------
@@ -15,8 +16,8 @@ Alternatively, you can build the gem yourself:
15
16
 
16
17
  Config
17
18
  ------
18
- Configuration options are specified in a YAML file (the root of your Git repository
19
- is searched for a file named `dandelion.yml` by default). Example:
19
+ Configuration options are specified in a YAML file (by default, the root of your
20
+ Git repository is searched for a file named `dandelion.yml`). Example:
20
21
 
21
22
  # Required
22
23
  scheme: sftp
@@ -38,18 +39,43 @@ by the given scheme.
38
39
 
39
40
  **SFTP**: `scheme: sftp`
40
41
 
41
- Required: `host`, `username`, `password`
42
- Optional: `path`, `exclude`
42
+ Required:
43
+
44
+ * `host`
45
+ * `username`
46
+ * `password`
47
+
48
+ Optional:
49
+
50
+ * `path`
51
+ * `exclude`
43
52
 
44
53
  **FTP**: `scheme: ftp`
45
54
 
46
- Required: `host`, `username`, `password`
47
- Optional: `path`, `exclude`, `passive` (defaults to true)
55
+ Required:
56
+
57
+ * `host`
58
+ * `username`
59
+ * `password`
60
+
61
+ Optional:
62
+
63
+ * `path`
64
+ * `exclude`
65
+ * `passive` (defaults to true)
48
66
 
49
67
  **Amazon S3**: `scheme: s3`
50
68
 
51
- Required: `access_key_id`, `secret_access_key`, `bucket_name`
52
- Optional: `path`, `exclude`
69
+ Required:
70
+
71
+ * `access_key_id`
72
+ * `secret_access_key`
73
+ * `bucket_name`
74
+
75
+ Optional:
76
+
77
+ * `path`
78
+ * `exclude`
53
79
 
54
80
  Usage
55
81
  -----
@@ -57,28 +83,30 @@ From within your Git repository, run:
57
83
 
58
84
  $ dandelion deploy
59
85
 
60
- Or:
86
+ This will deploy the local `HEAD` revision to the location specified in the config
87
+ file. Dandelion keeps track of the currently deployed revision so that only files
88
+ which have been added/changed/deleted need to be transferred.
61
89
 
62
- $ dandelion deploy path/to/config.yml
63
-
64
- This will deploy the local `HEAD` revision to the server specified in the config
65
- file. Dandelion keeps track of the most recently deployed revision so that only
66
- files which have changed since the last deployment need to be transferred.
90
+ You can specify the revision you wish to deploy and Dandelion will determine which
91
+ files need to be transferred:
92
+
93
+ $ dandelion deploy <revision>
67
94
 
68
95
  For a more complete summary of usage options, run:
69
96
 
70
97
  $ dandelion -h
71
- Usage: dandelion [options] [[command] [options]] [config_file]
98
+ Usage: dandelion [options] <command> [<args>]
72
99
  -v, --version Display the current version
73
100
  -h, --help Display this screen
74
101
  --repo=[REPO] Use the given repository
102
+ --config=[CONFIG] Use the given configuration file
75
103
 
76
104
  Available commands:
77
105
  deploy
78
106
  status
107
+
108
+ Note that when specifying the repository or configuration file, the given paths
109
+ are relative to the current working directory (not the repository root). To see
110
+ the options for a particular command, run:
79
111
 
80
- Or:
81
-
82
- $ dandelion COMMAND -h
83
-
84
- To see the options for a given command.
112
+ $ dandelion <command> -h
@@ -2,6 +2,6 @@
2
2
 
3
3
  $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
4
4
 
5
- require 'dandelion/cli'
5
+ require 'dandelion/application'
6
6
 
7
- Dandelion::Cli::Main.execute(ARGV.dup)
7
+ Dandelion::Application.execute(ARGV.dup)
@@ -0,0 +1,73 @@
1
+ require 'dandelion/command'
2
+
3
+ module Dandelion
4
+ class Application
5
+ class << self
6
+ def execute(args)
7
+ new(args).execute
8
+ end
9
+ end
10
+
11
+ def initialize(args)
12
+ @args = args
13
+ @options = {}
14
+ @parser = OptionParser.new
15
+ end
16
+
17
+ def execute
18
+ global = Command::Base.parser(@options)
19
+ parse(global)
20
+
21
+ begin
22
+ name = @args.shift
23
+ command = Command::Base.create(name)
24
+ parse(command.parser(@options))
25
+ rescue Command::InvalidCommandError
26
+ log.fatal("Invalid command: #{name}")
27
+ log.fatal(global.help)
28
+ log.fatal("Available commands:")
29
+ log.fatal(Command::Base.commands.map { |name| " #{name}" }.join("\n"))
30
+ exit 1
31
+ end
32
+
33
+ prepare
34
+ validate
35
+
36
+ command.new(@options) do |cmd|
37
+ cmd.setup(@args) if cmd.respond_to?(:setup)
38
+ cmd.execute
39
+ end
40
+ end
41
+
42
+ def log
43
+ Dandelion.logger
44
+ end
45
+
46
+ private
47
+
48
+ def parse(parser)
49
+ begin
50
+ parser.order!(@args)
51
+ rescue OptionParser::InvalidOption => e
52
+ log.fatal(e.to_s.capitalize)
53
+ log.fatal(parser.help)
54
+ exit 1
55
+ end
56
+ end
57
+
58
+ def prepare
59
+ @options[:config] ||= File.join(@options[:repo], 'dandelion.yml')
60
+ end
61
+
62
+ def validate
63
+ unless File.exists?(File.join(@options[:repo], '.git'))
64
+ log.fatal("Not a git repository: #{@options[:repo]}")
65
+ exit 1
66
+ end
67
+ unless File.exists?(@options[:config])
68
+ log.fatal("Could not find file: #{@options[:config]}")
69
+ exit 1
70
+ end
71
+ end
72
+ end
73
+ end
@@ -13,7 +13,7 @@ module Dandelion
13
13
  end
14
14
  end
15
15
 
16
- class Backend
16
+ class Base
17
17
  class << self
18
18
  @@backends = {}
19
19
 
@@ -2,7 +2,7 @@ require 'dandelion/backend'
2
2
 
3
3
  module Dandelion
4
4
  module Backend
5
- class FTP < Backend
5
+ class FTP < Backend::Base
6
6
  scheme 'ftp'
7
7
 
8
8
  def initialize(config)
@@ -2,7 +2,7 @@ require 'dandelion/backend'
2
2
 
3
3
  module Dandelion
4
4
  module Backend
5
- class S3 < Backend
5
+ class S3 < Backend::Base
6
6
  scheme 's3'
7
7
  gems 'aws-s3'
8
8
 
@@ -3,7 +3,7 @@ require 'pathname'
3
3
 
4
4
  module Dandelion
5
5
  module Backend
6
- class SFTP < Backend
6
+ class SFTP < Backend::Base
7
7
  scheme 'sftp'
8
8
  gems 'net-sftp'
9
9
 
@@ -0,0 +1,118 @@
1
+ require 'dandelion'
2
+ require 'dandelion/backend'
3
+ require 'dandelion/deployment'
4
+ require 'dandelion/git'
5
+ require 'dandelion/version'
6
+ require 'optparse'
7
+ require 'yaml'
8
+
9
+ module Dandelion
10
+ module Command
11
+ class InvalidCommandError < StandardError; end
12
+
13
+ class Base
14
+ class << self
15
+ @@commands = {}
16
+
17
+ def command(name)
18
+ @@commands[name] = self
19
+ end
20
+
21
+ def create(name)
22
+ require_commands
23
+ raise InvalidCommandError unless @@commands.include?(name)
24
+ @@commands[name]
25
+ end
26
+
27
+ def commands
28
+ @@commands.keys
29
+ end
30
+
31
+ def require_commands
32
+ Dir.glob(File.join(File.dirname(__FILE__), 'command', '*.rb')) { |file| require file }
33
+ end
34
+
35
+ def parser(options)
36
+ OptionParser.new do |opts|
37
+ opts.banner = 'Usage: dandelion [options] <command> [<args>]'
38
+
39
+ opts.on('-v', '--version', 'Display the current version') do
40
+ puts "Dandelion #{Dandelion::VERSION}"
41
+ exit
42
+ end
43
+
44
+ opts.on('-h', '--help', 'Display this screen') do
45
+ require_commands
46
+ puts opts
47
+ puts "\nAvailable commands:"
48
+ puts commands.map { |name| " #{name}" }.join("\n")
49
+ exit
50
+ end
51
+
52
+ options[:repo] = closest_repo(File.expand_path('.'))
53
+ opts.on('--repo=[REPO]', 'Use the given repository') do |repo|
54
+ options[:repo] = File.expand_path(repo)
55
+ end
56
+
57
+ options[:config] = nil
58
+ opts.on('--config=[CONFIG]', 'Use the given configuration file') do |config|
59
+ options[:config] = File.expand_path(config)
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def closest_repo(dir)
67
+ if File.exists?(File.join(dir, '.git'))
68
+ dir
69
+ else
70
+ File.dirname(dir) != dir && closest_repo(File.dirname(dir)) || File.expand_path('.')
71
+ end
72
+ end
73
+ end
74
+
75
+ def initialize(options)
76
+ @options = options
77
+ @config = YAML.load_file(@options[:config])
78
+ @repo = Git::Repo.new(@options[:repo])
79
+
80
+ yield(self) if block_given?
81
+ end
82
+
83
+ protected
84
+
85
+ def log
86
+ Dandelion.logger
87
+ end
88
+
89
+ def backend
90
+ begin
91
+ backend = Backend::Base.create(@config)
92
+ log.info("Connecting to #{backend}")
93
+ backend
94
+ rescue Backend::MissingDependencyError => e
95
+ log.fatal("The '#{@config['scheme']}' scheme requires additional gems:")
96
+ log.fatal(e.gems.map { |name| " #{name}" }.join("\n"))
97
+ log.fatal("Please install the gems: gem install #{e.gems.join(' ')}")
98
+ exit 1
99
+ rescue Backend::UnsupportedSchemeError
100
+ log.fatal("Unsupported scheme: #{@config['scheme']}")
101
+ exit 1
102
+ end
103
+ end
104
+
105
+ def deployment(revision, backend = nil)
106
+ begin
107
+ backend ||= backend()
108
+ options = { :excldue => @config['exclude'], :revision => revision, :dry => @options[:dry] }
109
+ Deployment::Deployment.create(@repo, backend, options)
110
+ rescue Git::DiffError
111
+ log.fatal('Error: could not generate diff')
112
+ log.fatal('Try merging remote changes before running dandelion again')
113
+ exit 1
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,55 @@
1
+ module Dandelion
2
+ module Command
3
+ class Deploy < Command::Base
4
+ command 'deploy'
5
+
6
+ class << self
7
+ def parser(options)
8
+ OptionParser.new do |opts|
9
+ opts.banner = 'Usage: deploy [options] [<revision>]'
10
+
11
+ options[:force] = false
12
+ opts.on('-f', '--force', 'Force deployment') do
13
+ options[:force] = true
14
+ end
15
+
16
+ options[:dry] = false
17
+ opts.on('--dry-run', 'Show what would have been deployed') do
18
+ options[:dry] = true
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def setup(args)
25
+ @revision = args.shift || 'HEAD'
26
+ end
27
+
28
+ def execute
29
+ begin
30
+ @deployment = deployment(@revision)
31
+ rescue Git::RevisionError
32
+ log.fatal("Invalid revision: #{@revision}")
33
+ exit 1
34
+ end
35
+
36
+ log.info("Remote revision: #{@deployment.remote_revision || '---'}")
37
+ log.info("Deploying revision: #{@deployment.local_revision}")
38
+
39
+ begin
40
+ @deployment.validate
41
+ rescue Deployment::FastForwardError
42
+ if !@options[:force]
43
+ log.warn('Warning: you are trying to deploy unpushed commits')
44
+ log.warn('This could potentially prevent others from being able to deploy')
45
+ log.warn('If you are sure you want to this, use the -f option to force deployment')
46
+ exit 1
47
+ end
48
+ end
49
+
50
+ @deployment.deploy
51
+ log.info("Deployment complete")
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,22 @@
1
+ module Dandelion
2
+ module Command
3
+ class Status < Command::Base
4
+ command 'status'
5
+
6
+ class << self
7
+ def parser(options)
8
+ OptionParser.new do |opts|
9
+ opts.banner = 'Usage: dandelion status'
10
+ end
11
+ end
12
+ end
13
+
14
+ def execute
15
+ @deployment = deployment('HEAD')
16
+
17
+ log.info("Remote revision: #{@deployment.remote_revision || '---'}")
18
+ log.info("Local HEAD revision: #{@deployment.local_revision}")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -7,20 +7,26 @@ module Dandelion
7
7
 
8
8
  class Deployment
9
9
  class << self
10
- def create(repo, backend, exclude = nil, revision = 'HEAD')
10
+ def create(repo, backend, options)
11
11
  begin
12
- DiffDeployment.new(repo, backend, exclude, revision)
12
+ DiffDeployment.new(repo, backend, options)
13
13
  rescue RemoteRevisionError
14
- FullDeployment.new(repo, backend, exclude, revision)
14
+ FullDeployment.new(repo, backend, options)
15
15
  end
16
16
  end
17
17
  end
18
18
 
19
- def initialize(repo, backend, exclude = nil, revision = 'HEAD')
19
+ def initialize(repo, backend, options = {})
20
20
  @repo = repo
21
21
  @backend = backend
22
- @exclude = exclude || []
23
- @tree = Git::Tree.new(@repo, revision)
22
+ @options = { :exclude => [], :revision => 'HEAD' }.merge(options)
23
+ @tree = Git::Tree.new(@repo, @options[:revision])
24
+
25
+ if @options[:dry]
26
+ # Stub out the destructive backend methods
27
+ def @backend.write(file, data); end
28
+ def @beckend.delete(file); end
29
+ end
24
30
  end
25
31
 
26
32
  def local_revision
@@ -35,10 +41,10 @@ module Dandelion
35
41
  @backend.write('.revision', local_revision)
36
42
  end
37
43
 
38
- def validate_state(remote = nil)
44
+ def validate
39
45
  begin
40
- if remote and fast_forwardable(remote)
41
- raise FastForwardError
46
+ @repo.remote_list.each do |remote|
47
+ raise FastForwardError if fast_forwardable(remote)
42
48
  end
43
49
  rescue Grit::Git::CommandFailed
44
50
  end
@@ -51,20 +57,20 @@ module Dandelion
51
57
  protected
52
58
 
53
59
  def exclude_file?(file)
54
- return @exclude.map { |e| file.start_with?(e) }.any?
60
+ return @options[:exclude].map { |e| file.start_with?(e) }.any?
55
61
  end
56
62
 
57
63
  private
58
64
 
59
65
  def fast_forwardable(remote)
60
- @repo.git.native(:remote, {:raise => true}, 'show', remote) =~ /fast-forward/i
66
+ !(@repo.git.native(:remote, {:raise => true}, 'show', remote) =~ /fast-forward/i).nil?
61
67
  end
62
68
  end
63
69
 
64
70
  class DiffDeployment < Deployment
65
- def initialize(repo, backend, exclude = nil, revision = 'HEAD')
66
- super(repo, backend, exclude, revision)
67
- @diff = Git::Diff.new(@repo, read_remote_revision, revision)
71
+ def initialize(repo, backend, options = {})
72
+ super(repo, backend, options)
73
+ @diff = Git::Diff.new(@repo, read_remote_revision, @options[:revision])
68
74
  end
69
75
 
70
76
  def remote_revision
@@ -3,6 +3,7 @@ require 'grit'
3
3
  module Dandelion
4
4
  module Git
5
5
  class DiffError < StandardError; end
6
+ class RevisionError < StandardError; end
6
7
 
7
8
  class Repo < Grit::Repo
8
9
  def initialize(dir)
@@ -54,6 +55,7 @@ module Dandelion
54
55
  def initialize(repo, revision)
55
56
  @repo = repo
56
57
  @commit = @repo.commit(revision)
58
+ raise RevisionError if @commit.nil?
57
59
  @tree = @commit.tree
58
60
  end
59
61
 
@@ -1,3 +1,3 @@
1
1
  module Dandelion
2
- VERSION = '0.2.3'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -87,7 +87,7 @@ class TestDiffDeployment < Test::Unit::TestCase
87
87
  @remote_revision = 'ff1f1d4bd0c99e1c9cca047c46b2194accf89504'
88
88
  @repo = MockRepo.new
89
89
  @backend = MockBackend.new(@remote_revision)
90
- @diff_deployment = Dandelion::Deployment::DiffDeployment.new(@repo, @backend, [], @head_revision)
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
metadata CHANGED
@@ -1,69 +1,92 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: dandelion
3
- version: !ruby/object:Gem::Version
4
- version: 0.2.3
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - Scott Nelson
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2011-06-08 00:00:00.000000000 -04:00
16
+
17
+ date: 2011-06-28 00:00:00 -04:00
13
18
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
16
21
  name: grit
17
- requirement: &2153656700 !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
18
24
  none: false
19
- requirements:
20
- - - ! '>='
21
- - !ruby/object:Gem::Version
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 4
31
+ - 1
22
32
  version: 2.4.1
23
33
  type: :runtime
24
- prerelease: false
25
- version_requirements: *2153656700
26
- - !ruby/object:Gem::Dependency
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
27
36
  name: mocha
28
- requirement: &2153656200 !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
29
39
  none: false
30
- requirements:
31
- - - ! '>='
32
- - !ruby/object:Gem::Version
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 9
46
+ - 12
33
47
  version: 0.9.12
34
48
  type: :development
35
- prerelease: false
36
- version_requirements: *2153656200
37
- - !ruby/object:Gem::Dependency
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
38
51
  name: net-sftp
39
- requirement: &2153683620 !ruby/object:Gem::Requirement
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
40
54
  none: false
41
- requirements:
42
- - - ! '>='
43
- - !ruby/object:Gem::Version
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 2
60
+ - 0
61
+ - 5
44
62
  version: 2.0.5
45
63
  type: :development
46
- prerelease: false
47
- version_requirements: *2153683620
48
- - !ruby/object:Gem::Dependency
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
49
66
  name: aws-s3
50
- requirement: &2153683160 !ruby/object:Gem::Requirement
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
51
69
  none: false
52
- requirements:
53
- - - ! '>='
54
- - !ruby/object:Gem::Version
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 0
75
+ - 6
76
+ - 0
55
77
  version: 0.6.0
56
78
  type: :development
57
- prerelease: false
58
- version_requirements: *2153683160
79
+ version_requirements: *id004
59
80
  description: Incremental Git repository deployment
60
- email:
81
+ email:
61
82
  - scottbnel@gmail.com
62
- executables:
83
+ executables:
63
84
  - dandelion
64
85
  extensions: []
86
+
65
87
  extra_rdoc_files: []
66
- files:
88
+
89
+ files:
67
90
  - .gitignore
68
91
  - Gemfile
69
92
  - README.md
@@ -71,11 +94,14 @@ files:
71
94
  - bin/dandelion
72
95
  - dandelion.gemspec
73
96
  - lib/dandelion.rb
97
+ - lib/dandelion/application.rb
74
98
  - lib/dandelion/backend.rb
75
99
  - lib/dandelion/backend/ftp.rb
76
100
  - lib/dandelion/backend/s3.rb
77
101
  - lib/dandelion/backend/sftp.rb
78
- - lib/dandelion/cli.rb
102
+ - lib/dandelion/command.rb
103
+ - lib/dandelion/command/deploy.rb
104
+ - lib/dandelion/command/status.rb
79
105
  - lib/dandelion/deployment.rb
80
106
  - lib/dandelion/git.rb
81
107
  - lib/dandelion/version.rb
@@ -114,29 +140,36 @@ files:
114
140
  has_rdoc: true
115
141
  homepage: http://github.com/scttnlsn/dandelion
116
142
  licenses: []
143
+
117
144
  post_install_message:
118
145
  rdoc_options: []
119
- require_paths:
146
+
147
+ require_paths:
120
148
  - lib
121
- required_ruby_version: !ruby/object:Gem::Requirement
149
+ required_ruby_version: !ruby/object:Gem::Requirement
122
150
  none: false
123
- requirements:
124
- - - ! '>='
125
- - !ruby/object:Gem::Version
126
- version: '0'
127
- required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ segments:
155
+ - 0
156
+ version: "0"
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
158
  none: false
129
- requirements:
130
- - - ! '>='
131
- - !ruby/object:Gem::Version
132
- version: '0'
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ segments:
163
+ - 0
164
+ version: "0"
133
165
  requirements: []
166
+
134
167
  rubyforge_project:
135
- rubygems_version: 1.6.2
168
+ rubygems_version: 1.3.7
136
169
  signing_key:
137
170
  specification_version: 3
138
- summary: dandelion-0.2.3
139
- test_files:
171
+ summary: dandelion-0.3.0
172
+ test_files:
140
173
  - test/fixtures/diff
141
174
  - test/fixtures/ls_tree
142
175
  - test/test_diff_deployment.rb
@@ -1,201 +0,0 @@
1
- require 'dandelion'
2
- require 'dandelion/backend'
3
- require 'dandelion/deployment'
4
- require 'dandelion/git'
5
- require 'dandelion/version'
6
- require 'optparse'
7
- require 'yaml'
8
-
9
- module Dandelion
10
- module Cli
11
- class Options
12
- def initialize
13
- @options = {}
14
- @config_file = nil
15
- @global = global_parser
16
- @commands = { 'deploy' => deploy_parser, 'status' => status_parser }
17
- @commands_help = "\nAvailable commands:\n #{@commands.keys.join("\n ")}"
18
- end
19
-
20
- def parse(args)
21
- order(@global, args)
22
- command = args.shift
23
- if command and @commands[command]
24
- order(@commands[command], args)
25
- end
26
-
27
- if @commands.key? command
28
- @config_file = args.shift.strip if args[0]
29
- command
30
- else
31
- if not @command.nil?
32
- puts "Invalid command: #{command}"
33
- end
34
- puts @global.help
35
- puts @commands_help
36
- exit
37
- end
38
- end
39
-
40
- def [](key)
41
- @options[key]
42
- end
43
-
44
- def []=(key, value)
45
- @options[key] = value
46
- end
47
-
48
- def config_file
49
- @config_file || File.join(@options[:repo], 'dandelion.yml')
50
- end
51
-
52
- private
53
-
54
- def closest_repo(dir)
55
- if File.exists?(File.join(dir, '.git'))
56
- dir
57
- else
58
- File.dirname(dir) != dir && closest_repo(File.dirname(dir))
59
- end
60
- end
61
-
62
- def global_parser
63
- OptionParser.new do |opts|
64
- opts.banner = 'Usage: dandelion [options] [[command] [options]]'
65
-
66
- opts.on('-v', '--version', 'Display the current version') do
67
- puts "Dandelion v#{Dandelion::VERSION}"
68
- exit
69
- end
70
-
71
- opts.on('-h', '--help', 'Display this screen') do
72
- puts opts
73
- puts @commands_help
74
- exit
75
- end
76
-
77
- @options[:repo] = closest_repo(File.expand_path('.'))
78
- opts.on('--repo=[REPO]', 'Use the given repository') do |repo|
79
- @options[:repo] = repo
80
- end
81
- end
82
- end
83
-
84
- def deploy_parser
85
- OptionParser.new do |opts|
86
- opts.banner = 'Usage: dandelion deploy [options]'
87
-
88
- @options[:force] = false
89
- opts.on('-f', '--force', 'Force deployment') do
90
- @options[:force] = true
91
- end
92
- end
93
- end
94
-
95
- def status_parser
96
- OptionParser.new do |opts|
97
- opts.banner = 'Usage: dandelion status'
98
- end
99
- end
100
-
101
- def order(parser, args)
102
- begin
103
- parser.order!(args)
104
- rescue OptionParser::InvalidOption => e
105
- puts e.to_s.capitalize
106
- puts parser.help
107
- exit
108
- end
109
- end
110
- end
111
-
112
- class Main
113
- class << self
114
- def execute(args)
115
- new(args).execute
116
- end
117
- end
118
-
119
- def initialize(args)
120
- @options = Options.new
121
- @command = @options.parse(args)
122
-
123
- validate_files
124
- @config = YAML.load_file(File.expand_path(@options.config_file))
125
- @repo = Git::Repo.new(File.expand_path(@options[:repo]))
126
- end
127
-
128
- def log
129
- Dandelion.logger
130
- end
131
-
132
- def execute
133
- deployment = deployment()
134
- log.info("Remote revision: #{deployment.remote_revision || '---'}")
135
- log.info("Local revision: #{deployment.local_revision}")
136
-
137
- if @command == 'status'
138
- exit
139
- elsif @command == 'deploy'
140
- validate_deployment(deployment)
141
- deployment.deploy
142
- log.info("Deployment complete")
143
- end
144
- end
145
-
146
- private
147
-
148
- def backend
149
- begin
150
- backend = Backend::Backend.create(@config)
151
- log.info("Connecting to: #{backend}")
152
- backend
153
- rescue Backend::MissingDependencyError => e
154
- log.fatal("The '#{@config['scheme']}' scheme requires additional gems:")
155
- log.fatal(' ' + e.gems.join("\n ") + "\n")
156
- log.fatal("Please install the gems: gem install #{e.gems.join(' ')}")
157
- exit
158
- rescue Backend::UnsupportedSchemeError
159
- log.fatal("Unsupported scheme: #{@config['scheme']}")
160
- exit
161
- end
162
- end
163
-
164
- def deployment
165
- begin
166
- Deployment::Deployment.create(@repo, backend, @config['exclude'])
167
- rescue Git::DiffError
168
- log.fatal('Error: could not generate diff')
169
- log.fatal('Try merging remote changes before running dandelion again')
170
- exit
171
- end
172
- end
173
-
174
- def validate_deployment(deployment)
175
- begin
176
- @repo.remote_list.each do |remote|
177
- deployment.validate_state(remote)
178
- end
179
- rescue Deployment::FastForwardError
180
- if !@options[:force]
181
- log.warn('Warning: you are trying to deploy unpushed commits')
182
- log.warn('This could potentially prevent others from being able to deploy')
183
- log.warn('If you are sure you want to this, use the -f option to force deployment')
184
- exit
185
- end
186
- end
187
- end
188
-
189
- def validate_files
190
- unless File.exists?(File.expand_path(File.join(@options[:repo], '.git')))
191
- log.fatal("Not a git repository: #{@options[:repo]}")
192
- exit
193
- end
194
- unless File.exists?(File.expand_path(@options.config_file))
195
- log.fatal("Could not find file: #{@options.config_file}")
196
- exit
197
- end
198
- end
199
- end
200
- end
201
- end