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
data/Rakefile ADDED
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ # Bundler is managing $LOAD_PATH, any gem needed by this Rakefile must be
4
+ # listed as a development dependency in the gemspec
5
+ require 'bundler/setup'
6
+ require 'bundler/gem_tasks'
7
+
8
+ require 'rbconfig'
9
+ WINDOWS = RbConfig::CONFIG['host_os'] =~ /msdos|mswin|win32|mingw/i unless defined?(WINDOWS)
10
+
11
+ require 'rspec/core/rake_task'
12
+ desc "Run RSpec"
13
+ RSpec::Core::RakeTask.new do |spec|
14
+ spec.pattern = 'spec/**/*_spec.rb'
15
+ end
16
+
17
+ require 'cucumber/rake/task'
18
+ Cucumber::Rake::Task.new do |t|
19
+
20
+ opts = []
21
+ opts << ["--color"]
22
+ opts << ["--format pretty"]
23
+ opts << ["--strict"]
24
+ opts << ["-r features"]
25
+ opts << ["--no-profile"]
26
+ opts << ["--tags ~@wip"]
27
+ opts << ["--tags ~@windows"] unless WINDOWS
28
+ opts << ["--tags ~@posix"] if WINDOWS
29
+
30
+ t.cucumber_opts = opts
31
+ end
32
+
33
+ desc "Run specs, both RSpec and Cucumber"
34
+ task :test => [:spec, :cucumber]
35
+
36
+ task :default => :test
37
+
38
+ namespace :doc do
39
+
40
+ doc_version = File.open(File.join(File.dirname(__FILE__), 'VERSION'), "r") { |f| f.read }
41
+ project_root = File.expand_path(File.dirname(__FILE__))
42
+ doc_destination = File.join(project_root, 'rdoc')
43
+
44
+ require 'yard'
45
+
46
+ YARD::Rake::YardocTask.new(:generate) do |yt|
47
+ yt.options = ['--output-dir', doc_destination,
48
+ '--title', "RepoManager #{doc_version} Documentation",
49
+ '--main', "README.markdown"
50
+ ]
51
+ end
52
+
53
+ desc "Remove generated documenation"
54
+ task :clean do
55
+ rm_r doc_destination if File.exists?(doc_destination)
56
+ end
57
+
58
+ desc "List undocumented objects"
59
+ task :undocumented do
60
+ system('yard stats --list-undoc')
61
+ end
62
+
63
+ end
64
+
65
+ # put the gemfiles task in the bundler dependency chain
66
+ task :build => [:gemfiles]
67
+ task :install => [:gemfiles]
68
+ task :release => [:gemfiles]
69
+
70
+ desc "Generate .gemfiles via 'git ls-files'"
71
+ task :gemfiles do
72
+ files = `git ls-files`
73
+
74
+ filename = File.join(File.dirname(__FILE__), '.gemfiles')
75
+ cached_files = nil
76
+ if File.exists?(filename)
77
+ puts ".gemfiles exists, reading..."
78
+ cached_files = File.open(filename, "rb") {|f| f.read}
79
+ end
80
+
81
+ if cached_files && cached_files.match("\r\n")
82
+ puts ".gemfiles using DOS EOL"
83
+ files.gsub!(/\n/, "\r\n")
84
+ end
85
+
86
+ if cached_files != files
87
+ puts ".gemfiles updating"
88
+ File.open(filename, 'wb') {|f| f.write(files)}
89
+ else
90
+ puts ".gemfiles update not required"
91
+ end
92
+
93
+ raise "unable to process .gemfiles" unless files
94
+ end
data/TODO.markdown ADDED
@@ -0,0 +1,15 @@
1
+ TODO
2
+ ====
3
+
4
+ * remove git gem and duplicate needed method. The use of ENV[] will break on
5
+ win32 and ruby 1.9.3
6
+ * add find action that finds a repo based on all strings in the repo config
7
+ * provide native repo completion via --completion option so the entire ARGV can
8
+ be scanned and passed back to bash. We have to load ruby anyway, might as
9
+ well provide completion for commands and options too.
10
+ * status should show summary at the end
11
+ * add feature tests for all combinations of XY result codes from 'git status --porcelain'
12
+ * status command should have option to show last commit information
13
+ * native git commands need to preserve ANSI escape codes for coloring
14
+ * add man page via markdown and ronn. Change 'help' action to call man
15
+ page if man available.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.1
data/bin/repo ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'repo_manager'
4
+ require 'optparse'
5
+ require 'term/ansicolor'
6
+ require 'fileutils'
7
+
8
+ available_actions = RepoManager::AVAILABLE_ACTIONS
9
+
10
+ banner = <<BANNER
11
+ repo: CLI for batch management of multiple Git repositories
12
+
13
+ Usage: repo [options] action [filters|action pass-through options] [options]
14
+ BANNER
15
+ banner << "\nActions: #{available_actions.join(' ')}\n" unless available_actions.empty?
16
+
17
+ help = banner
18
+ help += <<HELP
19
+
20
+ Use 'repo help' for help on actions.
21
+
22
+ repo help task
23
+ repo help git
24
+
25
+ Examples:
26
+
27
+ repo list my_repo1 my_repo2
28
+ repo list --filter=my_repo1,my_repo2
29
+
30
+ repo list --filter=my.*
31
+ repo list my.*
32
+
33
+ repo --verbose --no-color config --list --filter=my_repo1,my_repo2
34
+ repo --verbose --no-color config core.autocrlf true filter=my.*
35
+ repo --verbose --no-color git config --list
36
+ repo --verbose --no-color config --list
37
+
38
+ Most Git commands can be passed directly, these are equivalent
39
+
40
+ repo add .
41
+ repo git add .
42
+
43
+ General options:
44
+ (use 'repo help action' for action options)
45
+
46
+ HELP
47
+
48
+ # get options from the command line, these options override both config files
49
+ # and defaults
50
+ options = {}
51
+ optparser = OptionParser.new do |opts|
52
+ opts.banner = help
53
+
54
+ opts.on("-T", "--tasks", "List tasks") do |t|
55
+ options[:tasks] = t
56
+ end
57
+
58
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
59
+ options[:verbose] = v
60
+ end
61
+
62
+ opts.on("-c", "--[no-]coloring [MODE]", "--[no-]color [MODE]", "ANSI color in output. MODE=AUTO (default) or ALWAYS") do |c|
63
+ options[:color] = c.nil? ? "AUTO" : c
64
+ options[:color].upcase! if options[:color]
65
+ unless [nil, false, "AUTO", "ALWAYS"].include?(options[:color])
66
+ puts "repo, invalid color option: #{options[:color]}"
67
+ exit 1
68
+ end
69
+ end
70
+
71
+ opts.on("--[no-]config FILE", "Load configuration options from FILE") do |file|
72
+ options[:config] = file
73
+ end
74
+
75
+ opts.on("--version", "Display current version and exit") do
76
+ puts "repo_manager, version " + RepoManager.version
77
+ exit 0
78
+ end
79
+
80
+ # no argument, shows at tail. This will print an options summary.
81
+ opts.on_tail("-h", "--help", "Show this message") do
82
+ puts opts
83
+ exit 0
84
+ end
85
+
86
+ end
87
+
88
+ # first pass, process until the action/subcommand, errors will be raised for
89
+ # invalid options that occur before the action/subcommand
90
+ begin
91
+ optparser.order!
92
+ rescue OptionParser::InvalidOption => e
93
+ puts "repo #{e}"
94
+ puts "repo --help for more information"
95
+ exit 1
96
+ end
97
+
98
+
99
+ # OptionParser is too helpful and matches short options with long. Before the
100
+ # second pass, replace short git options with long because we can't tell
101
+ # OptionParser to stop matching short options to long.
102
+ ARGV.each_with_index do |arg, index|
103
+ # check and replace each short git option
104
+ # that could be parsed by main parser
105
+ case arg
106
+ when '-m'
107
+ ARGV[index] = '--message'
108
+ end
109
+ end
110
+
111
+ # second pass find all global options that may come after the action/subcommand
112
+ # and its args, no errors raised, validity will be checked by action parser
113
+ argv = []
114
+ while unknown_arg = ARGV.shift
115
+ argv << unknown_arg
116
+ begin
117
+ optparser.order!
118
+ rescue OptionParser::InvalidOption => e
119
+ # put unknown args back on ARGV
120
+ e.recover(ARGV)
121
+ end
122
+ end
123
+
124
+ # settings from config file, if it exists, will not overwrite command line options
125
+ settings = RepoManager::Settings.new(FileUtils.pwd, options)
126
+ color = settings.options[:color]
127
+
128
+ # add summary of general options for use by action help commands
129
+ configuration = settings.to_hash
130
+
131
+ if STDOUT.isatty || (color == 'ALWAYS')
132
+ Term::ANSIColor::coloring = color
133
+
134
+ if color && RepoManager::WINDOWS
135
+ unless ENV['ANSICON']
136
+ begin
137
+ require 'Win32/Console/ANSI'
138
+ rescue LoadError
139
+ Term::ANSIColor::coloring = false
140
+ STDERR.puts 'WARNING: You must "gem install win32console" (1.2.0 or higher) or use the ANSICON driver (https://github.com/adoxa/ansicon) to get color output on MRI/Windows'
141
+ end
142
+ end
143
+ end
144
+
145
+ else
146
+ Term::ANSIColor::coloring = false
147
+ end
148
+
149
+ app = RepoManager::App.new(argv, configuration)
150
+ app.option_parser = optparser
151
+ app.execute
data/cucumber.yml ADDED
@@ -0,0 +1,28 @@
1
+ <%
2
+ require 'rbconfig'
3
+ WINDOWS = RbConfig::CONFIG['host_os'] =~ /msdos|mswin|win32|mingw/i unless defined?(WINDOWS)
4
+
5
+ opts = []
6
+ opts << ["--color"]
7
+ opts << ["--format pretty"]
8
+ opts << ["--strict"]
9
+ opts << ["-r features"]
10
+ opts << ["--no-profile"]
11
+ opts << ["--tags ~@wip"]
12
+ opts << ["--tags ~@windows"] unless WINDOWS
13
+ opts << ["--tags ~@posix"] if WINDOWS
14
+
15
+ std_opts = opts.join(' ')
16
+ rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
17
+ rerun_opts = rerun.to_s.strip.empty? ? "--format pretty" : "--format pretty #{rerun}"
18
+
19
+ %>
20
+ # define cucumber profiles
21
+ #
22
+ # Example usage:
23
+ #
24
+ # bundle exec cucumber --profile wip
25
+ ---
26
+ default: <%= std_opts %> -r features
27
+ wip: --tags @wip:3 --wip -r features
28
+ rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip -r features
@@ -0,0 +1,2 @@
1
+ /repo.log
2
+ /repo_manager/tmp
@@ -0,0 +1,420 @@
1
+ Creating the RepoManager Saved Game Backup Configuration
2
+ ====================================================
3
+
4
+ > NOTE for Windows users
5
+ >
6
+ > The given instruction are intended for a Bash shell. Bash is not
7
+ > required to use RepoManager, but it does make using the command prompt much
8
+ > more flexible and productive. The MSYS distribution of portable Git
9
+ > includes a lean and stable Bash environment.
10
+
11
+ Initial configuration
12
+ ---------------------
13
+
14
+ ### install repo_manager
15
+
16
+ gem install repo_manager
17
+
18
+ ### create configuration
19
+
20
+ The following commands were used to create this example folder from
21
+ scratch.
22
+
23
+ mkdir -p examples/pc_saved_game_backup && cd examples/pc_saved_game_backup
24
+
25
+ Create configuration structure with the built-in 'generate:init' task
26
+
27
+ > NOTE
28
+ >
29
+ > We are creating a local configuration. For a global configuration, you would
30
+ > execute the init command in your home folder
31
+
32
+ repo generate:init repo_manager
33
+
34
+ We are going to keep this under version control
35
+
36
+ git init
37
+ git add .
38
+ git commit -m "initial commit"
39
+ echo "/repo.log" > .gitignore
40
+ echo "/repo_manager/tmp" >> .gitignore
41
+
42
+ Change the generated paths from absolute to relative to make this example
43
+ portable.
44
+
45
+ diff --git a/repo_manager/repo.conf b/repo_manager/repo.conf
46
+ index ce4418d..3cc6dbe 100644
47
+ --- a/repo_manager/repo.conf
48
+ +++ b/repo_manager/repo.conf
49
+ @@ -11,7 +11,7 @@ options:
50
+ folders:
51
+
52
+ # main repo configuration files
53
+ - assets : /home/robert/examples/pc_saved_game_backup/repo_manager/assets
54
+ + assets : assets
55
+
56
+ #
57
+ # repo user tasks, file extentions can be '.rb' or '.thor'
58
+ @@ -26,7 +26,7 @@ folders:
59
+ #
60
+ # c:/dat/condenser/tasks
61
+ #
62
+ - tasks : /home/robert/examples/pc_saved_game_backup/repo_manager/tasks
63
+ + tasks : tasks
64
+
65
+ # git commands must be whitelisted
66
+ commands:
67
+ @@ -55,7 +55,7 @@ logging:
68
+ name : logfile
69
+ level : info
70
+ truncate : true
71
+ - filename : '/home/robert/examples/pc_saved_game_backup/repo_manager/repo.log'
72
+ + filename : 'repo.log'
73
+ layout:
74
+ type : Pattern
75
+ pattern : '[%d] %l %c : %m\n'
76
+
77
+ ### add sample data
78
+
79
+ Add a few example save game folder. These folders would normally be
80
+ scattered over the file system.
81
+
82
+ mines
83
+
84
+ mkdir -p saved_games/mines/saves
85
+
86
+ # profile data will not be stored in the Git repo since it may differ from PC to PC
87
+ echo "# dummy profile data" > mines/my_profile.ini
88
+
89
+ echo "# dummy save" > saved_games/mines/saves/save1
90
+ echo "# dummy save" > saved_games/mines/saves/save2
91
+
92
+ hearts
93
+
94
+ mkdir -p saved_games/hearts
95
+
96
+ echo "# dummy save" > saved_games/hearts/save1
97
+ echo "# dummy save" > saved_games/hearts/save2
98
+
99
+ ### create remote folder
100
+
101
+ This folder will act as a remote to hold bare Git repositories. These
102
+ repos will store backups of our game saves, normally, this folder would be
103
+ on a remote server, NAS, or Drop Box like service.
104
+
105
+ mkdir remote
106
+
107
+ remote/.gitignore
108
+
109
+ *
110
+ !/.gitignore
111
+
112
+
113
+ Create the specialized 'git init' task
114
+ --------------------------------------
115
+
116
+ User tasks can be added directly to the repo_manager/tasks folder. This one
117
+ is 'repo_manager/tasks/remote.rb'. It doesn't use any RepoManager specific features,
118
+ instead, it calls git directly via Thor's 'run' command. Adding the script
119
+ this way will keep this related functionality with this specific RepoManager
120
+ configuration. Run 'repo -T' to see a full list of built-in tasks as well
121
+ as user defined tasks.
122
+
123
+ repo_manager/tasks/remote.rb
124
+
125
+ require 'fileutils'
126
+
127
+ module RepoManager
128
+
129
+ class Generate < Thor
130
+
131
+ # full path to the remote folder
132
+ REMOTE = File.expand_path('remote')
133
+
134
+ # Create, add, and commit the contents of the current working directory and
135
+ # then push it to a predefined remote folder
136
+ #
137
+ # @example From the repo working
138
+ #
139
+ # cd ~/my_repo_name
140
+ # repo generate:remote my_repo_name
141
+ #
142
+ # @example Specify the path to the working folder
143
+ #
144
+ # repo generate:remote my_repo_name --path=/path/to/my_repo_name
145
+
146
+ method_option :remote, :type => :string, :desc => "remote folder or git host, defaults to '#{REMOTE}'"
147
+ method_option :path, :type => :string, :desc => "path to working folder, defaults to CWD"
148
+
149
+ desc "remote REPO_NAME", "init a git repo in CWD and push to remote '#{REMOTE}'"
150
+ def remote(name)
151
+ path = options[:path] || FileUtils.pwd
152
+ remote = options[:remote] || "#{File.join(REMOTE, name + '.git')}"
153
+
154
+ Dir.chdir path do
155
+ run("git init")
156
+
157
+ # core config with windows in mind but works fine on POSIX
158
+ run("git config core.autocrlf false")
159
+ run("git config core.filemode false")
160
+ exit $?.exitstatus if ($?.exitstatus > 1)
161
+
162
+ # add everthing and commit
163
+ run("git add .")
164
+ run("git commit --message #{shell_quote('initial commit')}")
165
+ exit $?.exitstatus if ($?.exitstatus > 1)
166
+
167
+ # remove old origin first, if it exists
168
+ run("git remote add origin #{remote}")
169
+ run("git config branch.master.remote origin")
170
+ run("git config branch.master.merge refs/heads/master")
171
+ exit $?.exitstatus if ($?.exitstatus > 1)
172
+ end
173
+
174
+ run("git clone --bare #{shell_quote(path)} #{remote}")
175
+ exit $?.exitstatus if ($?.exitstatus > 1)
176
+
177
+ say "init done on '#{name}'", :green
178
+ end
179
+
180
+ end
181
+ end
182
+
183
+ ### add remotes
184
+
185
+ In one step, we will initialize a new git repository with the working folder's
186
+ content and push to a new bare repository for backup.
187
+
188
+ > Normally, you don't need to specify the --path if you are already in the
189
+ > working folder and the repo_manager can find its global config file. For this
190
+ > example, we are using relative paths and will specify the working folder
191
+ > on the command line via the '--path' option.
192
+
193
+ repo generate:remote mines --path=saved_games/mines/saves
194
+ repo generate:remote hearts --path=saved_games/hearts
195
+
196
+ ### create the repo_manager asset configuration files
197
+
198
+ repo add:asset saved_games/mines/saves --name=mines --force
199
+ repo add:asset saved_games/hearts --force
200
+
201
+
202
+ Create the specialized Update task
203
+ ----------------------------------
204
+
205
+ repo_manager/tasks/update.rb
206
+
207
+ module RepoManager
208
+ class Action < Thor
209
+ namespace :action
210
+ include Thor::Actions
211
+ include RepoManager::ThorHelper
212
+
213
+ class_option :force, :type => :boolean, :desc => "Force overwrite and answer 'yes' to any prompts"
214
+
215
+ method_option :repos, :type => :string, :desc => "Restrict update to comma delimited list of repo names", :banner => "repo1,repo2"
216
+ method_option :message, :type => :string, :desc => "Override 'automatic commit' message"
217
+ method_option 'no-push', :type => :boolean, :default => false, :desc => "Force overwrite of existing config file"
218
+
219
+ desc "update", "run repo add -A, repo commit, and repo push on all modified repos"
220
+ def update
221
+
222
+ initial_filter = options[:repos] ? "--repos=#{options[:repos]}" : ""
223
+ output = run("repo status --short --unmodified=HIDE --no-verbose --no-color #{initial_filter}", :capture => true)
224
+
225
+ case $?.exitstatus
226
+ when 0
227
+ say 'no changed repos', :green
228
+ else
229
+
230
+ unless output
231
+ say "failed to successfully run 'repo status'", :red
232
+ exit $?.exitstatus
233
+ end
234
+
235
+ repos = []
236
+ output = output.split("\n")
237
+ while line = output.shift
238
+ st,repo = line.split("\t")
239
+ repos << repo
240
+ end
241
+ filter = repos.join(',')
242
+
243
+ unless options[:force]
244
+ say "Repo(s) '#{filter}' have changed."
245
+ unless ask("Add, commit and push them? (y/n)") == 'y'
246
+ say "aborting"
247
+ exit 0
248
+ end
249
+ end
250
+
251
+ say "updating #{filter}"
252
+
253
+ run "repo add -A --no-verbose --repos #{filter}"
254
+ exit $?.exitstatus if ($?.exitstatus > 1)
255
+
256
+ commit_message = options[:message] || "automatic commit @ #{Time.now}"
257
+ run "repo commit --message=#{shell_quote(commit_message)} --no-verbose --repos #{filter}"
258
+ exit $?.exitstatus if ($?.exitstatus > 1)
259
+
260
+ unless options['no-push']
261
+ run "repo push --no-verbose --repos #{filter}"
262
+ exit $?.exitstatus if ($?.exitstatus > 1)
263
+ end
264
+
265
+ say "update finished", :green
266
+ end
267
+
268
+ end
269
+ end
270
+ end
271
+
272
+ ### whitelist non-default Git commands
273
+
274
+ Only a small subset of non-destructive git commands are enabled by default. We will
275
+ add the commands needed by our user task to the commands whitelist.
276
+
277
+ Add 'push, add, and commit' to the commands whitelist
278
+
279
+ diff --git a/repo_manager/repo.conf b/repo_manager/repo.conf
280
+ index 3cc6dbe..226b8c0 100644
281
+ --- a/repo_manager/repo.conf
282
+ +++ b/repo_manager/repo.conf
283
+ @@ -36,6 +36,9 @@ commands:
284
+ - ls-files
285
+ - show
286
+ - status
287
+ +- push
288
+ +- add
289
+ +- commit
290
+
291
+ Testing user tasks with Cucumber
292
+ --------------------------------------
293
+
294
+ ### Add a Gemfile for use by Bundler
295
+
296
+ repo_manager/Gemfile
297
+
298
+ source "http://rubygems.org"
299
+
300
+ gem "repo_manager"
301
+
302
+ gem "bundler", ">= 1.0.14"
303
+ gem "rspec", ">= 2.6.0"
304
+ gem "cucumber", "~> 1.0"
305
+ gem "aruba", "= 0.4.5"
306
+
307
+ gem "win32console", :platforms => [:mingw, :mswin]
308
+
309
+ ### Install the dependencies
310
+
311
+ gem install bundler
312
+
313
+ cd repo_manager
314
+ bundle
315
+
316
+ ### Add Cucumber features and support files
317
+
318
+ repo_manager/features/tasks/update.feature
319
+
320
+ > NOTE: This is an excerpt, see the file for the full listing of functional tests
321
+
322
+ @announce
323
+ Feature: Automatically commit and update multiple repos
324
+
325
+ Background: Test repositories and a valid config file
326
+ Given a repo in folder "test_path_1" with the following:
327
+ | filename | status | content |
328
+ | .gitignore | C | |
329
+ And a repo in folder "test_path_2" with the following:
330
+ | filename | status | content |
331
+ | .gitignore | C | |
332
+ And a file named "repo.conf" with:
333
+ """
334
+ ---
335
+ folders:
336
+ assets : repo/asset/configuration/files
337
+ """
338
+ And the folder "repo/asset/configuration/files" with the following asset configurations:
339
+ | name | path |
340
+ | test1 | test_path_1 |
341
+ | test2 | test_path_2 |
342
+
343
+
344
+ Scenario: No uncommitted changes
345
+ When I run `repo action:update`
346
+ Then the output should contain:
347
+ """
348
+ no changed repos
349
+ """
350
+
351
+ ...
352
+
353
+ repo_manager/features/support/steps.rb
354
+
355
+ require 'repo_manager/test/base_steps'
356
+ require 'repo_manager/test/asset_steps'
357
+ require 'repo_manager/test/repo_steps'
358
+
359
+ repo_manager/features/support/env.rb
360
+
361
+ require 'repo_manager'
362
+ require 'aruba/cucumber'
363
+ require 'rspec/expectations'
364
+
365
+ repo_manager/features/support/aruba.rb
366
+
367
+ require 'aruba/api'
368
+ require 'fileutils'
369
+
370
+ module Aruba
371
+ module Api
372
+
373
+ # override aruba avoid 'current_ruby' call and make sure
374
+ # that binary run on Win32 without the binstubs
375
+ def detect_ruby(cmd)
376
+ wrapper = which('repo')
377
+ cmd = cmd.gsub(/^repo/, "ruby -S #{wrapper}") if wrapper
378
+ cmd
379
+ end
380
+ end
381
+ end
382
+
383
+ ### Run tests
384
+
385
+ bundle exec cucumber
386
+
387
+ Bash completion
388
+ ---------------
389
+
390
+ Handy functions for use under Bash. These work fine on Win32 using
391
+ Git-Bash.
392
+
393
+ ### CD command for working folders
394
+
395
+ rpushd: repo pushd (push directory). Wrapper for 'pushd'.
396
+
397
+ ### Completion for repo names
398
+
399
+ rcd: repo cd (change directory). Wrapper for 'cd', allows for simple cd <repo
400
+ name> to the working folder on the filesystem referenced by the 'path'
401
+ configuration variable.
402
+
403
+ Source these functions in your .bashrc
404
+
405
+ function rcd(){ cd "$(repo --match=ONE --no-color path $@)"; }
406
+ function rpushd(){ pushd "$(repo path --match=ONE --no-color $@)"; }
407
+ alias rpopd="popd"
408
+
409
+ # provide completion for repo names
410
+ function _repo_names()
411
+ {
412
+ local cur opts prev
413
+ COMPREPLY=()
414
+ cur="${COMP_WORDS[COMP_CWORD]}"
415
+ opts=`repo list --list=name --no-color`
416
+
417
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
418
+ return 0
419
+ }
420
+ complete -F _repo_names rcd rpushd repo