repo_manager 0.7.1

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