repo_manager 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/.gemfiles +115 -0
  2. data/.gitattributes +1 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +3 -0
  5. data/.yardopts +11 -0
  6. data/Gemfile +11 -0
  7. data/Gemfile.lock +99 -0
  8. data/Guardfile +63 -0
  9. data/HISTORY.markdown +12 -0
  10. data/LICENSE +20 -0
  11. data/README.markdown +192 -0
  12. data/Rakefile +94 -0
  13. data/TODO.markdown +15 -0
  14. data/VERSION +1 -0
  15. data/bin/repo +151 -0
  16. data/cucumber.yml +28 -0
  17. data/examples/pc_saved_game_backup/.gitignore +2 -0
  18. data/examples/pc_saved_game_backup/INSTALL.markdown +420 -0
  19. data/examples/pc_saved_game_backup/README.markdown +108 -0
  20. data/examples/pc_saved_game_backup/remote/.gitignore +2 -0
  21. data/examples/pc_saved_game_backup/repo_manager/Gemfile +12 -0
  22. data/examples/pc_saved_game_backup/repo_manager/Gemfile.lock +66 -0
  23. data/examples/pc_saved_game_backup/repo_manager/assets/.gitignore +2 -0
  24. data/examples/pc_saved_game_backup/repo_manager/features/support/aruba.rb +15 -0
  25. data/examples/pc_saved_game_backup/repo_manager/features/support/env.rb +11 -0
  26. data/examples/pc_saved_game_backup/repo_manager/features/support/steps.rb +3 -0
  27. data/examples/pc_saved_game_backup/repo_manager/features/tasks/update.feature +144 -0
  28. data/examples/pc_saved_game_backup/repo_manager/global/default/asset.conf +2 -0
  29. data/examples/pc_saved_game_backup/repo_manager/repo.conf +64 -0
  30. data/examples/pc_saved_game_backup/repo_manager/tasks/.gitignore +0 -0
  31. data/examples/pc_saved_game_backup/repo_manager/tasks/remote.rb +57 -0
  32. data/examples/pc_saved_game_backup/repo_manager/tasks/update.rb +65 -0
  33. data/examples/pc_saved_game_backup/saved_games/hearts/save1 +1 -0
  34. data/examples/pc_saved_game_backup/saved_games/hearts/save2 +1 -0
  35. data/examples/pc_saved_game_backup/saved_games/mines/my_profile.ini +1 -0
  36. data/examples/pc_saved_game_backup/saved_games/mines/saves/save1 +1 -0
  37. data/examples/pc_saved_game_backup/saved_games/mines/saves/save2 +1 -0
  38. data/features/actions/git.feature +296 -0
  39. data/features/actions/help.feature +53 -0
  40. data/features/actions/list.feature +624 -0
  41. data/features/actions/path.feature +195 -0
  42. data/features/actions/status.feature +261 -0
  43. data/features/actions/task.feature +127 -0
  44. data/features/assets/configuration.feature +204 -0
  45. data/features/assets/rendering.feature +42 -0
  46. data/features/assets/user_attributes.feature +98 -0
  47. data/features/bin.feature +42 -0
  48. data/features/logger.feature +218 -0
  49. data/features/settings.feature +240 -0
  50. data/features/support/aruba.rb +15 -0
  51. data/features/support/env.rb +11 -0
  52. data/features/support/steps.rb +3 -0
  53. data/features/tasks/add/asset.feature +178 -0
  54. data/features/tasks/generate/init.feature +56 -0
  55. data/lib/repo_manager.rb +36 -0
  56. data/lib/repo_manager/actions.rb +8 -0
  57. data/lib/repo_manager/actions/action_helper.rb +39 -0
  58. data/lib/repo_manager/actions/app_action.rb +30 -0
  59. data/lib/repo_manager/actions/base_action.rb +296 -0
  60. data/lib/repo_manager/actions/git_action.rb +113 -0
  61. data/lib/repo_manager/actions/help_action.rb +52 -0
  62. data/lib/repo_manager/actions/list_action.rb +123 -0
  63. data/lib/repo_manager/actions/path_action.rb +22 -0
  64. data/lib/repo_manager/actions/status_action.rb +192 -0
  65. data/lib/repo_manager/actions/task_action.rb +71 -0
  66. data/lib/repo_manager/app.rb +116 -0
  67. data/lib/repo_manager/assets.rb +3 -0
  68. data/lib/repo_manager/assets/app_asset.rb +15 -0
  69. data/lib/repo_manager/assets/asset_accessors.rb +67 -0
  70. data/lib/repo_manager/assets/asset_configuration.rb +137 -0
  71. data/lib/repo_manager/assets/asset_manager.rb +72 -0
  72. data/lib/repo_manager/assets/base_asset.rb +199 -0
  73. data/lib/repo_manager/assets/repo_asset.rb +30 -0
  74. data/lib/repo_manager/core.rb +2 -0
  75. data/lib/repo_manager/core/array.rb +21 -0
  76. data/lib/repo_manager/core/hash.rb +83 -0
  77. data/lib/repo_manager/errors.rb +10 -0
  78. data/lib/repo_manager/extensions/hash.rb +86 -0
  79. data/lib/repo_manager/git.rb +2 -0
  80. data/lib/repo_manager/git/lib.rb +69 -0
  81. data/lib/repo_manager/git/status.rb +196 -0
  82. data/lib/repo_manager/logger.rb +39 -0
  83. data/lib/repo_manager/settings.rb +98 -0
  84. data/lib/repo_manager/tasks.rb +3 -0
  85. data/lib/repo_manager/tasks/add/asset.rb +213 -0
  86. data/lib/repo_manager/tasks/generate/init.rb +42 -0
  87. data/lib/repo_manager/tasks/generate/templates/config/repo.conf.tt +61 -0
  88. data/lib/repo_manager/tasks/generate/templates/init/assets/.gitignore +0 -0
  89. data/lib/repo_manager/tasks/generate/templates/init/global/default/asset.conf +2 -0
  90. data/lib/repo_manager/tasks/generate/templates/init/tasks/.gitignore +0 -0
  91. data/lib/repo_manager/tasks/task_manager.rb +166 -0
  92. data/lib/repo_manager/tasks/thor_helper.rb +29 -0
  93. data/lib/repo_manager/test/asset_steps.rb +19 -0
  94. data/lib/repo_manager/test/base_steps.rb +152 -0
  95. data/lib/repo_manager/test/repo_api.rb +41 -0
  96. data/lib/repo_manager/test/repo_steps.rb +83 -0
  97. data/lib/repo_manager/test/test_api.rb +88 -0
  98. data/lib/repo_manager/views.rb +2 -0
  99. data/lib/repo_manager/views/app_view.rb +15 -0
  100. data/lib/repo_manager/views/base_view.rb +137 -0
  101. data/lib/repo_manager/views/templates/css/basic.css +26 -0
  102. data/lib/repo_manager/views/templates/default.erb +40 -0
  103. data/lib/repo_manager/views/templates/default.slim +37 -0
  104. data/lib/repo_manager/views/view_helper.rb +55 -0
  105. data/repo_manager.gemspec +75 -0
  106. data/spec/basic_app/actions/action_helper_spec.rb +54 -0
  107. data/spec/basic_app/assets/base_asset_spec.rb +210 -0
  108. data/spec/basic_app/core_spec.rb +78 -0
  109. data/spec/basic_app/settings_spec.rb +64 -0
  110. data/spec/basic_app/views/view_helper_spec.rb +28 -0
  111. data/spec/basic_gem/aruba_helper_spec.rb +33 -0
  112. data/spec/basic_gem/basic_gem_spec.rb +84 -0
  113. data/spec/basic_gem/gemspec_spec.rb +68 -0
  114. data/spec/repo_manager/git_spec.rb +31 -0
  115. data/spec/spec_helper.rb +25 -0
  116. metadata +472 -0
@@ -0,0 +1,196 @@
1
+ module RepoManager
2
+
3
+ # Simplified version of ruby-git's class that uses Git's 'status --porcelain' command
4
+ #
5
+ # The --porcelain switch is useful since it handles ignored files and ignores
6
+ # non-commitable changes. Speed is not a big concern. There is only one
7
+ # call needed to the Git binary. No plumbing commands are used.
8
+ class Status
9
+ include Enumerable
10
+
11
+ # repo status unchanged/clean
12
+ CLEAN = 0
13
+
14
+ # bitfields for status
15
+ NOPATH = 1
16
+ INVALID = 2
17
+ CHANGED = 4
18
+ ADDED = 8
19
+ DELETED = 16
20
+ UNTRACKED = 32
21
+ UNMERGED = 64
22
+
23
+ attr_reader :files
24
+
25
+ def initialize(scm)
26
+ @files = {}
27
+ @scm = scm
28
+ construct_status
29
+ end
30
+
31
+ # @return [Numeric] 0 if CLEAN or bitfield with status: CHANGED | UNTRACKED | ADDED | DELETED | UNMERGED
32
+ def bitfield
33
+ # M ? A D U
34
+ (changed? ? CHANGED : 0) |
35
+ (untracked? ? UNTRACKED : 0) |
36
+ (added? ? ADDED : 0) |
37
+ (deleted? ? DELETED : 0) |
38
+ (unmerged? ? UNMERGED : 0)
39
+ end
40
+
41
+ def changed
42
+ @files.select { |k, f| f.type == 'M' }
43
+ end
44
+
45
+ def added
46
+ @files.select { |k, f| f.type == 'A' }
47
+ end
48
+
49
+ def deleted
50
+ @files.select { |k, f| f.type == 'D' }
51
+ end
52
+
53
+ def untracked
54
+ @files.select { |k, f| f.type == '?' }
55
+ end
56
+
57
+ def unmerged
58
+ @files.select { |k, f| f.type == 'U' }
59
+ end
60
+
61
+ # @return [Boolean] false unless a file has been modified/changed
62
+ def changed?
63
+ !changed.empty?
64
+ end
65
+
66
+ # @return [Boolean] false unless a file has been added
67
+ def added?
68
+ !added.empty?
69
+ end
70
+
71
+ # @return [Boolean] false unless a file has been deleted
72
+ def deleted?
73
+ !deleted.empty?
74
+ end
75
+
76
+ # @return [Boolean] false unless there is a new/untracked file
77
+ def untracked?
78
+ !untracked.empty?
79
+ end
80
+
81
+ # @return [Boolean] false unless there is an unmerged file
82
+ def unmerged?
83
+ !unmerged.empty?
84
+ end
85
+
86
+ def [](file)
87
+ @files[file]
88
+ end
89
+
90
+ def each
91
+ @files.each do |k, file|
92
+ yield file
93
+ end
94
+ end
95
+
96
+ class StatusFile
97
+ attr_accessor :path, :type
98
+
99
+ def initialize(hash)
100
+ @path = hash[:path]
101
+ @type = hash[:type]
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def construct_status
108
+ # XY filename
109
+ # Y = working tree
110
+ #
111
+ # From git 1.7+ documentation
112
+ #
113
+ # X Y Meaning
114
+ # -------------------------------------------------
115
+ # [MD] not updated
116
+ # M [ MD] updated in index
117
+ # A [ MD] added to index
118
+ # D [ MD] deleted from index
119
+ # R [ MD] renamed in index
120
+ # C [ MD] copied in index
121
+ # [MARC] index and work tree matches
122
+ # [ MARC] M work tree changed since index
123
+ # [ MARC] D deleted in work tree
124
+ # -------------------------------------------------
125
+ # D D unmerged, both deleted
126
+ # A U unmerged, added by us
127
+ # U D unmerged, deleted by them
128
+ # U A unmerged, added by them
129
+ # D U unmerged, deleted by us
130
+ # A A unmerged, both added
131
+ # U U unmerged, both modified
132
+ # -------------------------------------------------
133
+ # ? ? untracked
134
+ # -------------------------------------------------
135
+ #
136
+ #
137
+ # simplify porcelain output:
138
+ #
139
+ # combine X and Y and boil down status returns to just five types,
140
+ # M ? A D U
141
+ #
142
+ # example output:
143
+ #
144
+ # output = [" M .gitignore", "R testing s.txt", "test space.txt", "?? new_file1.txt"]
145
+ #
146
+ # show working folder status with just five status condtions
147
+ #
148
+ # exact matches for untracked and unmerged
149
+ #
150
+ # '??' => 'untracked' [untracked.blue]
151
+ # 'DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU' => 'unmerged' [unmerged.red.bold]
152
+ #
153
+ # added with working folder clear
154
+ #
155
+ # /R./ => 'renamed' [added.green], special handling required
156
+ # /A /, /M /, /D /, /R /, /C / => 'added' [added.green]
157
+ #
158
+ # everything else can be described by working folder status character
159
+ #
160
+ # /.D/ => 'deleted', [deleted.yellow]
161
+ # /.M/ => 'modified', [changed.red]
162
+
163
+ output = @scm.lib.native('status', ['--porcelain', '-z']).split("\000")
164
+ while line = output.shift
165
+ next unless line && line.length > 3
166
+ file_hash = nil
167
+ # first two chars, XY format
168
+ st = line[0..1]
169
+ # skip the space
170
+ filename = line[3..-1]
171
+
172
+ # renamed/copied files 'to -> from', 'from' will be on the next line,
173
+ # shift it off as we don't track this
174
+ output.shift if st.match(/[R|C]/)
175
+
176
+ case st
177
+ when '??'
178
+ file_hash = {:type => '?', :path => filename}
179
+ when 'DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU'
180
+ file_hash = {:type => 'U', :path => filename}
181
+ when 'A ', 'M ', 'D ', 'R ', 'C '
182
+ file_hash = {:type => 'A', :path => filename}
183
+ when /.D/
184
+ file_hash = {:type => 'D', :path => filename}
185
+ when /.M/
186
+ file_hash = {:type => 'M', :path => filename}
187
+ else
188
+ raise "fatal error: unknown git status condition: '#{st}'"
189
+ end
190
+
191
+ @files[filename] = StatusFile.new(file_hash) if (file_hash && filename)
192
+ end
193
+ end
194
+
195
+ end
196
+ end
@@ -0,0 +1,39 @@
1
+ require 'logging'
2
+ include Logging.globally
3
+
4
+ module RepoManager
5
+ module Logger
6
+
7
+ class Manager
8
+
9
+ def initialize(config_filename=nil, yaml_key=nil, configuration={})
10
+
11
+ options = configuration[:options] || {}
12
+
13
+ if config_filename && yaml_key && configuration.has_key?(yaml_key)
14
+ Logging::Config::YamlConfigurator.load(config_filename, yaml_key.to_s)
15
+ else
16
+ # setup a default root level STDOUT logger
17
+ format = {:pattern => '%-5l %c: %m\n'}
18
+ format = format.merge(:color_scheme => 'default') if options[:color]
19
+ Logging.appenders.stdout('stdout', :layout => Logging.layouts.pattern(format), :level => :info)
20
+ Logging.logger.root.add_appenders('stdout')
21
+ end
22
+
23
+ # if verbose, all defined loggers are set to debug level
24
+ if options[:verbose]
25
+ Logging.appenders.each do |appender|
26
+ appender.level = :debug
27
+ end
28
+ end
29
+
30
+ # debug
31
+ #Logging.show_configuration
32
+ #logger.error "error"
33
+ #logger.warn "warn"
34
+ #logger.info "info"
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,98 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+ require 'fileutils'
4
+ require 'repo_manager/extensions/hash'
5
+
6
+ module RepoManager
7
+
8
+ # Access setting via symbolized keys or using accessor methods
9
+ #
10
+ # @example
11
+ #
12
+ # settings = Settings.new(FileUtils.pwd, {:config => 'some/file.yml'})
13
+ #
14
+ # verbose = settings.to_hash[:options] ? settings.to_hash[:options][:verbose] : false
15
+ #
16
+ # equivalent to:
17
+ #
18
+ # verbose = settings.options ? settings.options.verbose : false
19
+ #
20
+ # @return [Hash], for pure hash use 'to_hash' instead
21
+ class Settings < Hash
22
+ include RepoManager::Extensions::MethodReader
23
+ include RepoManager::Extensions::MethodWriter
24
+
25
+ def initialize(working_dir=nil, options={})
26
+ @working_dir = working_dir || FileUtils.pwd
27
+ @configuration = configure(options)
28
+
29
+ # call super without args
30
+ super *[]
31
+
32
+ self.merge!(@configuration)
33
+ end
34
+
35
+ private
36
+
37
+ # read options from YAML config
38
+ def configure(options)
39
+
40
+ # config file default options
41
+ configuration = {
42
+ :options => {
43
+ :verbose => false,
44
+ :color => 'AUTO',
45
+ :short => false,
46
+ :unmodified => 'HIDE',
47
+ :match => 'ALL',
48
+ :list => 'ALL'
49
+ },
50
+ :commands => [
51
+ 'diff',
52
+ 'grep',
53
+ 'log',
54
+ 'ls-files',
55
+ 'show',
56
+ 'status'
57
+ ]
58
+ }
59
+
60
+ # set default config if not given on command line
61
+ config = options[:config]
62
+ if config.nil?
63
+ config = [
64
+ File.join(@working_dir, "repo.conf"),
65
+ File.join(@working_dir, ".repo.conf"),
66
+ File.join(@working_dir, "repo_manager", "repo.conf"),
67
+ File.join(@working_dir, ".repo_manager", "repo.conf"),
68
+ File.join(@working_dir, "config", "repo.conf"),
69
+ File.expand_path(File.join("~", ".repo.conf")),
70
+ File.expand_path(File.join("~", "repo.conf")),
71
+ File.expand_path(File.join("~", "repo_manager", "repo.conf")),
72
+ File.expand_path(File.join("~", ".repo_manager", "repo.conf"))
73
+ ].detect { |filename| File.exists?(filename) }
74
+ end
75
+
76
+ if config && File.exists?(config)
77
+ # load options from the config file, overwriting hard-coded defaults
78
+ logger.debug "reading configuration file: #{config}"
79
+ config_contents = YAML.load(ERB.new(File.open(config, "rb").read).result)
80
+ configuration.merge!(config_contents.symbolize_keys!) if config_contents && config_contents.is_a?(Hash)
81
+ else
82
+ # user specified a config file?, no error if user did not specify config file
83
+ raise "config file not found" if options[:config]
84
+ end
85
+
86
+ # store the original full config filename for later use
87
+ configuration[:configuration_filename] = config
88
+
89
+ configuration.recursively_symbolize_keys!
90
+
91
+ # the command line options override options read from the config file
92
+ configuration[:options].merge!(options)
93
+ configuration
94
+ end
95
+
96
+ end
97
+
98
+ end
@@ -0,0 +1,3 @@
1
+ require 'repo_manager/tasks/task_manager'
2
+
3
+ RepoManager::TaskManager.new.load_tasks
@@ -0,0 +1,213 @@
1
+ require 'repo_manager'
2
+ require 'repo_manager/tasks/task_manager'
3
+ require 'repo_manager/actions/action_helper'
4
+ require 'pathname'
5
+
6
+ module RepoManager
7
+
8
+ module GenerateHelper
9
+
10
+ def asset_name_to_config_file(name=nil)
11
+ raise ArgumentError, "missing name" unless name
12
+ raise "unable to find configuration key ':folders'" unless configuration[:folders]
13
+ raise "unable to find configuration key ':folders => :assets'" unless configuration[:folders][:assets]
14
+
15
+ folder = configuration.folders[:assets]
16
+ unless folder
17
+ say "unable to find folder conf key ':folders => :assets', please set key"
18
+ exit 1
19
+ end
20
+
21
+ unless Pathname.new(folder).absolute?
22
+ base_folder = File.dirname(configuration[:configuration_filename])
23
+ end
24
+
25
+ file = File.join(base_folder, folder, name, "asset.conf")
26
+ end
27
+
28
+ end
29
+
30
+ module AddHelper
31
+
32
+ def process_discovered_assets(discovered_assets=[])
33
+
34
+ say_status "configuring", "setting discovered asset attributes"
35
+ discovered_assets.each do |discovered_asset|
36
+ folder = File.dirname(asset_name_to_config_file(discovered_asset.name))
37
+ discovered_asset.configuration.folder = folder
38
+ end
39
+
40
+ if options[:refresh]
41
+ if existing_assets.any? && discovered_assets.any?
42
+ say_status "merging", "merging existing asset attributes"
43
+ discovered_assets.each do |discovered_asset|
44
+ existing_asset = existing_assets.detect do |existing_asset|
45
+ existing_asset.name == discovered_asset.name
46
+ end
47
+ discovered_asset.attributes.merge!(existing_asset.attributes) if existing_asset
48
+ end
49
+ end
50
+ else
51
+ say_status "comparing", "looking at existing asset names"
52
+ discovered_assets.delete_if do |asset|
53
+ result = false
54
+ if File.exists?(asset.configuration.folder)
55
+ logger.debug "#{asset.name} asset name already exists, skipping"
56
+ result = true
57
+ end
58
+ result
59
+ end
60
+
61
+ say_status "comparing", "looking at existing asset paths"
62
+ discovered_assets.delete_if do |asset|
63
+ result = false
64
+ existing_asset = existing_assets.detect do |existing_asset|
65
+ existing_asset.path && asset.path && (File.expand_path(existing_asset.path) == File.expand_path(asset.path))
66
+ end
67
+ if (existing_asset)
68
+ logger.debug "#{asset.name} path matches existing asset #{existing_asset.name}, skipping"
69
+ result = true
70
+ end
71
+ result
72
+ end
73
+ end
74
+
75
+ unless discovered_assets.any?
76
+ say "no assets found for updating"
77
+ exit 0
78
+ end
79
+
80
+ # list the new assets found
81
+ say "Discovered assets"
82
+ discovered_assets.each do |asset|
83
+ say_status :found, "%-40s path => '%s'" % [asset.name, relative_path(asset.path)] , :green
84
+ end
85
+
86
+ # prompt the user
87
+ say
88
+ unless options[:force]
89
+ exit 0 unless (ask("Found #{discovered_assets.size} asset(s), write the configuration file(s) (y/n)?") == 'y')
90
+ end
91
+
92
+ # write the assets
93
+ say
94
+ discovered_assets.each do |asset|
95
+
96
+ say_status :creating, "repo_manager configuration file for #{asset.name}", :green
97
+ logger.debug "writing asset #{asset.name} to #{asset.configuration.folder}"
98
+ asset.attributes.merge!(:parent => "../global/default")
99
+ save_writable_attributes(asset, asset.attributes)
100
+
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ class Add < Thor
108
+ namespace :add
109
+ include Thor::Actions
110
+ include RepoManager::ThorHelper
111
+ include RepoManager::GenerateHelper
112
+ include RepoManager::AddHelper
113
+ include ::RepoManager::ActionHelper
114
+
115
+ # adds :quiet, :skip, :pretent, :force
116
+ add_runtime_options!
117
+
118
+ method_option :filter, :type => :array, :aliases => "-f", :desc => "List of regex folder name filters"
119
+ method_option :refresh, :type => :boolean, :aliases => "-r", :desc => "Refresh existing blank attributes"
120
+
121
+ desc "assets FOLDER", "generate multiple config files by searching a folder, one level deep, for git repositories"
122
+ def assets(folder)
123
+ say_status "collecting", "collecting top level folder names"
124
+ discovered_assets = []
125
+ filters = options[:filter] || ['.*']
126
+ # Thor does not allow comma separated array options, fix that here
127
+ filters = filters.first.to_s.split(',') if filters.length == 1
128
+ Dir.glob( File.join(folder, '*/') ).each do |repo_folder|
129
+ logger.debug "filters: #{filters.inspect}"
130
+ next unless filters.find {|filter| repo_folder.match(/#{filter}/)}
131
+ next unless File.exists?(File.join(repo_folder, '.git/'))
132
+
133
+ # check existing assets for path match, if found, use existing name instead of the generated name
134
+ existing = existing_assets.detect do |existing_asset|
135
+ existing_asset.path && repo_folder && (File.expand_path(existing_asset.path) == File.expand_path(repo_folder))
136
+ end
137
+
138
+ if (existing)
139
+ name = existing.name
140
+ else
141
+ name = File.basename(repo_folder)
142
+ end
143
+
144
+ asset = ::RepoManager::RepoAsset.new(name)
145
+ asset.path = File.expand_path(repo_folder)
146
+
147
+ discovered_assets << asset
148
+ end
149
+
150
+ process_discovered_assets(discovered_assets)
151
+ end
152
+
153
+ method_option :name, :type => :string, :aliases => "-n", :desc => "given the asset a specific name"
154
+
155
+ desc "asset FOLDER", "generate a single repo asset file given a working folder"
156
+ def asset(folder)
157
+ discovered_assets = []
158
+ unless File.exists?(File.join(folder, '.git/'))
159
+ say_status :error, "unable to find '.git' folder in working folder '#{folder}'", :red
160
+ exit 1
161
+ end
162
+
163
+ # check existing assets for path match, if found, use existing name instead of the generated name
164
+ existing = existing_assets.detect do |existing_asset|
165
+ existing_asset.path && folder && (File.expand_path(existing_asset.path) == File.expand_path(folder))
166
+ end
167
+
168
+ if (existing)
169
+ name = existing.name
170
+ if options[:name]
171
+ say_status :error, "asset already exists under a different name '#{name}'", :red
172
+ exit 1
173
+ end
174
+ else
175
+ name = options[:name]
176
+ name ||= File.basename(folder)
177
+ end
178
+
179
+ asset = ::RepoManager::RepoAsset.new(name)
180
+ asset.path = File.expand_path(folder)
181
+
182
+ discovered_assets << asset
183
+ process_discovered_assets(discovered_assets)
184
+ end
185
+
186
+ private
187
+
188
+ # where to start looking, required by the template method
189
+ def self.source_root
190
+ File.dirname(__FILE__)
191
+ end
192
+
193
+ # write only the attributes that we have set
194
+ def save_writable_attributes(asset, attributes)
195
+ valid_keys = [:parent, :path]
196
+ accessable_attributes = {}
197
+ attributes.each do |key, value|
198
+ accessable_attributes[key] = value.dup if valid_keys.include?(key)
199
+ end
200
+ asset.configuration.save(accessable_attributes)
201
+ end
202
+
203
+ def existing_assets
204
+ return @existing_assets if @existing_assets
205
+ asset_options = {}
206
+ asset_options.merge!(:assets_folder => configuration[:folders][:assets]) if configuration[:folders]
207
+ asset_options.merge!(:base_folder => File.dirname(configuration[:configuration_filename])) if configuration[:configuration_filename]
208
+
209
+ @existing_assets = ::RepoManager::AssetManager.new.assets(asset_options)
210
+ end
211
+
212
+ end
213
+ end