six-updater 0.10.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.
data/LICENSE ADDED
@@ -0,0 +1,3 @@
1
+ == six-arma-updater
2
+
3
+ Put appropriate LICENSE for your project here.
data/README ADDED
@@ -0,0 +1,3 @@
1
+ == six-arma-updater
2
+
3
+ You should document your project here.
@@ -0,0 +1,49 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+
6
+ require 'rubygems'
7
+ require 'rake'
8
+ require 'rake/clean'
9
+ require 'rake/gempackagetask'
10
+ require 'rake/rdoctask'
11
+ require 'rake/testtask'
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = 'six-updater'
15
+ s.version = '0.10.1'
16
+ s.has_rdoc = true
17
+ s.extra_rdoc_files = ['README', 'LICENSE']
18
+ s.summary = 'Your summary here'
19
+ s.description = s.summary
20
+ s.author = 'Sickboy'
21
+ s.email = 'sb@dev-heaven.net'
22
+ s.add_dependency('log4r', '>= 1.1.0')
23
+ s.add_dependency('six-rsync', '>= 0.1.0')
24
+ s.add_dependency('win32-process', '>= 0.6.0')
25
+ s.add_dependency('win32-shortcut', '>= 0.2.3')
26
+ # s.executables = ['your_executable_here']
27
+ s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,spec}/**/*") + Dir.glob("lib/*/**/*.rb")
28
+ s.require_path = "lib"
29
+ s.bindir = "bin"
30
+ end
31
+
32
+ Rake::GemPackageTask.new(spec) do |p|
33
+ p.gem_spec = spec
34
+ p.need_tar = true
35
+ p.need_zip = true
36
+ end
37
+
38
+ Rake::RDocTask.new do |rdoc|
39
+ files =['README', 'LICENSE', 'lib/**/*.rb']
40
+ rdoc.rdoc_files.add(files)
41
+ rdoc.main = "README" # page to start on
42
+ rdoc.title = "six-arma-updater Docs"
43
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
44
+ rdoc.options << '--line-numbers'
45
+ end
46
+
47
+ Rake::TestTask.new do |t|
48
+ t.test_files = FileList['test/**/*.rb']
49
+ end
@@ -0,0 +1,51 @@
1
+ require 'six/updater/options'
2
+ require 'six/updater'
3
+
4
+ # TODO: Should exec outside the module
5
+ module Six
6
+ module Updater
7
+ if not defined?(Ocra)
8
+ initialize
9
+ mods = []
10
+ puts
11
+ log.info "Processing:"
12
+ @config[:folders].each do |config|
13
+ # TODO: Only display message when there are actually todo's
14
+ puts
15
+ log.info "= #{config[:folder]}"
16
+ mod = Mod.new(config, @config)
17
+ mods << mod
18
+ @todo.each { |t| mod.send t }
19
+ #mod.install
20
+ end
21
+
22
+ puts
23
+ log.info "Postprocessing:"
24
+ @config[:folders].each_with_index do |config,index|
25
+ mod = mods[index]
26
+ if mod.changed || @config[:force]
27
+ # TODO: Only display message when there are actually todo's
28
+ puts
29
+ log.info "= #{config[:folder]}"
30
+ @second_todo.each { |t| mod.send t }
31
+ end
32
+ end
33
+
34
+ puts
35
+ log.info "Processing general tasks:"
36
+ @general_todo.each { |t| Six::Updater.send t }
37
+
38
+ puts
39
+ log.info "Ready!"
40
+
41
+ if @config[:wait]
42
+ puts
43
+ log.info "Please press ENTER to exit"
44
+ STDIN.gets
45
+ end
46
+ else
47
+ gem 'git'
48
+ require 'git'
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,459 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'yaml'
4
+ require 'fileutils'
5
+ require 'win32/registry'
6
+
7
+ gem 'win32-shortcut'
8
+ require 'win32/shortcut'
9
+ gem 'log4r'
10
+ require 'log4r'
11
+ gem 'six-rsync'
12
+ require 'six/rsync'
13
+ gem 'win32-process'
14
+ require 'win32/process'
15
+
16
+ require 'digest/md5'
17
+ require 'six/updater/mod'
18
+ #require 'sixupdater/gitrepo'
19
+ require 'six/updater/rsyncrepo'
20
+
21
+ include Six::Repositories
22
+
23
+ # Language fix for alternative localizations
24
+ if RUBY_VERSION == "1.9.1"
25
+ if defined?(Ocra)
26
+ (Dir[File.join($:[-2], 'enc/*.so')] + Dir[File.join($:[-2], 'enc/trans/*.so')]).each do |file|
27
+ require file
28
+ end
29
+ end
30
+ #EXCLUDE = [12]
31
+ #(1..16).each { |i| require "enc/iso_8859_#{i}" unless EXCLUDE.include? i }
32
+ #require 'enc/windows_1251'
33
+ end
34
+
35
+ if RUBY_VERSION == "1.8.7"
36
+ class Array
37
+ def sample
38
+ self[rand self.size]
39
+ end
40
+ end
41
+ end
42
+
43
+ module Six
44
+ # TODO: Evaluate if this module should be a class instead?
45
+ module Updater
46
+ VERSION = '0.10.1'
47
+ COMPONENT = 'six-updater'
48
+
49
+ # Configuration
50
+ ARMA2 = ['SOFTWARE\\Bohemia Interactive Studio\\ArmA 2', 'MAIN']
51
+ ARMA2_ALT = ['SOFTWARE\\Bohemia Interactive\\ArmA 2', 'InstallPath']
52
+ ARMA2_STEAM = ['SOFTWARE\\SOFTWARE\\Valve\\Steam\\', 'InstallPath']
53
+ # Fix for WinXP
54
+ BASE_PATH = if ENV['OCRA_EXECUTABLE']
55
+ if ENV['OCRA_EXECUTABLE'][/(.*)[\/|\\].*/]
56
+ p = $1
57
+ p.gsub!('\\', '/')
58
+ p
59
+ else
60
+ p = Dir.pwd
61
+ p.gsub!('\\', '/')
62
+ p
63
+ end
64
+ elsif defined?(TAR2RUBYSCRIPT)
65
+ if oldlocation
66
+ oldlocation
67
+ else
68
+ Dir.pwd
69
+ end
70
+ else
71
+ p = Dir.pwd
72
+ p.gsub!('\\', '/')
73
+ p
74
+ end
75
+ TOOL_PATH = File.join(BASE_PATH, 'tools')
76
+ LOG_LIMIT = 9999
77
+ # TODO: Allow downloader yml instead?
78
+ # TODO: Auto update always etc? :D
79
+
80
+ @general_todo, @second_todo = [], []
81
+
82
+ bpath = BASE_PATH.clone
83
+ bpath.gsub!('/', '\\')
84
+ tpath = "#{File.join(bpath, 'tools')};#{File.join(bpath, 'tools', 'bin')}"
85
+ tpath.gsub!('/', '\\')
86
+ ENV['PATH'] = "#{ENV['PATH']};#{bpath};#{tpath}"
87
+
88
+ # Create config hash with default settings
89
+ @config = Hash.new
90
+ @config[:app_params] = '-noSplash -noFilePatching'
91
+ @config[:app_exe] = 'arma2.exe'
92
+ @config[:depth] = 1
93
+ @config[:folders] = []
94
+ @config[:server] = Hash.new
95
+ @config[:server_path] = File.join(BASE_PATH, 'servers')
96
+ @config[:verbose] = false
97
+
98
+ # FIXME: Solve this properly!
99
+ @config[:defaultactions] = [:install]
100
+ @config[:defaultgeneralactions] = [:createshortcut]
101
+ @config[:defaultsecondactions] = [:changelog, :cleanup]
102
+
103
+ # Create loggers
104
+ @@log = Log4r::Logger.new(COMPONENT)
105
+ format1 = if defined?(DEBUG)
106
+ Log4r::PatternFormatter.new(:pattern => "[%l] %d: %m", :date_pattern => '%H:%M:%S')
107
+ else
108
+ Log4r::PatternFormatter.new(:pattern => "%m")
109
+ end
110
+ format2 = Log4r::PatternFormatter.new(:pattern => "[%l] %c %d: %m", :date_pattern => '%H:%M:%S')
111
+
112
+
113
+ if not defined?(Ocra)
114
+ # Create Outputters
115
+ path = File.join(BASE_PATH, 'logs')
116
+ FileUtils::mkdir_p path
117
+ o_file = Log4r::FileOutputter.new "#{COMPONENT}-file",
118
+ 'level' => 0, # All
119
+ :filename => File.join(path, "#{COMPONENT}.log"),
120
+ 'formatter' => format2
121
+ #:maxsize => 1024
122
+
123
+ @@log.outputters << o_file
124
+
125
+ o_out = Log4r::StdoutOutputter.new "#{COMPONENT}-stdout",
126
+ 'level' => 2, # no DEBUG
127
+ 'formatter' => format1
128
+
129
+ o_err = Log4r::StderrOutputter.new "#{COMPONENT}-stderr",
130
+ 'level' => 4, # Error and Up
131
+ 'formatter' => format1
132
+
133
+ @@log.outputters << o_out << o_err
134
+ end
135
+
136
+ module_function
137
+ def log
138
+ @@log
139
+ end
140
+
141
+ def wait
142
+ STDIN.gets
143
+ end
144
+
145
+ def sure?
146
+ log.info "Are you really sure? Please press Enter to continue, or close the application to abort."
147
+ wait
148
+ end
149
+
150
+ def zipcheck
151
+ begin
152
+ %x[7z]
153
+ rescue
154
+ log.warn "7-zip not found, attempting download!"
155
+ system "six-downloader.exe \"#{File.join(BASE_PATH, 'six-downloader_7z.yml').gsub('/', '\\')}\" --output \"#{TOOL_PATH.gsub('/', '\\')}\""
156
+ file = File.join(TOOL_PATH, "7z.exe")
157
+ unless FileTest.exist?(file)
158
+ log.error "ERROR: #{file} not found, please download from http://7-zip.org/download.html '7-Zip Command Line Version'"
159
+ wait
160
+ Process.exit
161
+ end
162
+ end
163
+ end
164
+
165
+ # General Tasks
166
+ def joingame
167
+ cmd = ""
168
+ cmd << " -connect=#{@config[:server][:address]}" if @config[:server][:address]
169
+ cmd << " -port=#{@config[:server][:port]}" if @config[:server][:port]
170
+ cmd << " -password=#{@config[:server][:password]}" if @config[:server][:password]
171
+ log.info "Starting the game with #{@config[:app_params]} -mod=#{@mods}#{cmd}"
172
+ cwd = Dir.pwd
173
+ Dir.chdir @config[:app_path]
174
+ system "\"#{@config[:app_exe]}\" #{@config[:app_params]} -mod=#{@mods}#{cmd}"
175
+ Dir.chdir cwd
176
+ end
177
+
178
+ def startgame
179
+ log.info "Starting the game with #{@config[:app_params]} -mod=#{@mods}"
180
+ #system "\"#{@config[:app_exe]}\" #{@config[:app_params]} -mod=#{@mods}"
181
+ begin
182
+ struct = Process.create(
183
+ :app_name => File.join(@config[:app_path], @config[:app_exe]),
184
+ :command_line => "#{@config[:app_params]} -mod=#{@mods}",
185
+ :creation_flags => Process::DETACHED_PROCESS,
186
+ :process_inherit => false,
187
+ :thread_inherit => false,
188
+ :cwd => @config[:app_path],
189
+ :inherit => false
190
+ #:environment => ""
191
+ )
192
+ rescue
193
+ log.warn "WARNING: Something went wrong starting the app: #{@config[:app_exe]}"
194
+ end
195
+ end
196
+
197
+ def shortcut(path)
198
+ cwd = Dir.pwd
199
+ Dir.chdir path
200
+ mods = @mods.clone
201
+ mods.gsub!(/\//, '-')
202
+ mods2 = @mods.clone
203
+ mods2.gsub!('/', '\\')
204
+ name = "Play ArmA2 with #{mods}.lnk"
205
+ Win32::Shortcut.new(name) do |shortcut|
206
+ shortcut.description = "Start Arma2 with #{mods2}"
207
+ shortcut.path = File.join(@config[:app_path], @config[:app_exe])
208
+ shortcut.working_directory = @config[:app_path]
209
+ shortcut.arguments = "#{@config[:app_params]} -mod=#{mods2}"
210
+ end
211
+ str = path.clone
212
+ str.gsub!('/', '\\')
213
+ log.info "Created shortcut (in #{str}): #{name}"
214
+ Dir.chdir cwd
215
+ end
216
+
217
+ def createshortcut
218
+ shortcut @config[:app_path]
219
+ end
220
+
221
+ def createdesktopshortcut
222
+ shortcut "#{ENV['USERPROFILE']}\\Desktop"
223
+ end
224
+
225
+ def uninstall
226
+ if @config[:server]
227
+ if @config[:server][:name]
228
+ log.info "#{@config[:server][:name]} server"
229
+ path = File.join(@config[:server_path], @config[:server][:name])
230
+ if FileTest.exist?(path)
231
+ log.warn "###########"
232
+ log.warn "# WARNING #"
233
+ log.warn "###########"
234
+ log.warn "You are about to uninstall the specified repository"
235
+ sure?
236
+ log.warn 'Uninstalling...'
237
+ FileUtils.rm_rf path
238
+ #FileUtils.rm_f @modconfig[:changelog] if FileTest.exists?(@modconfig[:changelog])
239
+ #FileUtils.rm_f @modconfig[:versionfile] if FileTest.exists?(@modconfig[:versionfile])
240
+ end
241
+ end
242
+ end
243
+ =begin
244
+ @config[:folders].each do |config|
245
+ log.info "= #{config[:folder]}"
246
+ mod = Mod.new(config, @config)
247
+ mod.uninstall
248
+ end
249
+ =end
250
+ end
251
+
252
+ def setup_git
253
+ unless @setup_git
254
+ @setup_git = true
255
+ # Verify git existence / find git
256
+ unless @config[:git_path]
257
+ @config[:git_path] = File.join(TOOL_PATH, 'Git', 'Cmd')
258
+ unless FileTest.exist?(@config[:git_path])
259
+ begin
260
+ %x[git]
261
+ @config[:git_path] = 'PATH'
262
+ rescue
263
+ zipcheck
264
+ log.warn "Git not configured, nor found in path... Downloading!"
265
+ # download from http://alturl.com/hn84 into name; git.7z
266
+ pwd = Dir.pwd
267
+ Dir.chdir TOOL_PATH
268
+ file = "portablegit.7z"
269
+ FileUtils.rm_f file if FileTest.exist?(file)
270
+ system "six-downloader.exe \"#{File.join(BASE_PATH, 'six-downloader_portablegit.yml').gsub('/', '\\')}\" --output \"#{TOOL_PATH.gsub('/', '\\')}\""
271
+ dir = File.join(TOOL_PATH, 'Git')
272
+ FileUtils.rm_rf dir if FileTest.exist?(dir)
273
+ FileUtils.mkdir_p dir
274
+ log.info "Unpacking Git..."
275
+ %x[7z x -oGit \"#{file}\"]
276
+ FileUtils.rm_f file if FileTest.exist?(file)
277
+ Dir.chdir pwd
278
+ end
279
+ end
280
+ end
281
+ unless @config[:git_path] == 'PATH'
282
+ path = @config[:git_path].clone
283
+ path.gsub!('/','\\')
284
+ ENV['PATH'] = "#{ENV['PATH']};#{path}" if path.size > 0 #unless ENV['PATH'][/git/] # ??
285
+ end
286
+
287
+ log.info "Using Git at: #{@config[:git_path]}"
288
+ puts
289
+
290
+ # FIXME: This is naughty but due to portable git config setting, and this module's git version check, we must run this after initialize :/
291
+ gem 'git'
292
+ require 'git'
293
+ end
294
+ end
295
+
296
+ def initialize
297
+ # Banner
298
+ log.info "Updater (v#{VERSION}) by Sickboy <sb_at_dev-heaven.net>"
299
+ log.info 'Run with --help for help'
300
+ log.warn "WARNING: Please make sure anything ArmA related has been closed / shutdown, incl explorer windows, etc"
301
+ log.debug "BASE_PATH: #{BASE_PATH}"
302
+ log.debug "TOOL_PATH: #{TOOL_PATH}"
303
+ log.debug "@config #{@config}"
304
+
305
+ # Process the config file
306
+ config = "#{COMPONENT}.yml"
307
+ config = File.join(BASE_PATH, config)
308
+ if ARGV[0]
309
+ # FIXME: This is not a very good catch..
310
+ if ARGV[0][/.*\..*/]
311
+ if FileTest.exist?(ARGV[0])
312
+ config = ARGV[0]
313
+ else
314
+ log.warn "#{ARGV[0]} not found, trying #{config}"
315
+ end
316
+ end
317
+ end
318
+
319
+ unless FileTest.exist?(config)
320
+ log.error "ERROR: #{config} config missing!"
321
+ wait
322
+ Process.exit
323
+ end
324
+
325
+ File.open(config) { |file| @config.merge!(YAML::load(file)) }
326
+ @config[:folders] = [] unless @config[:folders]
327
+ @config[:server][:folders] = [] unless @config[:server][:folders]
328
+
329
+ # Parses CLI Parameters, merges configuration and gets todo list
330
+ parse_options
331
+
332
+ # Backwards compatibility
333
+ @config[:app_path] = @config[:armapath] if @config[:armapath]
334
+ @config[:app_exe] = @config[:armaexe] if @config[:armaexe]
335
+ @config[:app_params] = @config[:armaparams] if @config[:armaparams]
336
+ @config[:git_path] = @config[:gitpath] if @config[:gitpath]
337
+ @config[:server_path] = @config[:serverpath] if @config[:serverpath]
338
+
339
+ # Determine arma distribution and location
340
+ unless @config[:app_path]
341
+ begin
342
+ @config[:app_path] = Win32::Registry.open(Win32::Registry::HKEY_LOCAL_MACHINE, ARMA2_STEAM[0])[ARMA2_STEAM[1]]
343
+ log.info "ArmA 2 Steam Distribution detected"
344
+ rescue
345
+ begin
346
+ @config[:app_path] = Win32::Registry.open(Win32::Registry::HKEY_LOCAL_MACHINE, ARMA2_ALT[0])[ARMA2_ALT[1]]
347
+ log.info "ArmA 2 Alt Distribution detected"
348
+ rescue
349
+ begin
350
+ @config[:app_path] = Win32::Registry.open(Win32::Registry::HKEY_LOCAL_MACHINE, ARMA2[0])[ARMA2[1]]
351
+ log.info "ArmA 2 Standard Distribution detected"
352
+ rescue
353
+ log.warn "No (known) ArmA 2 Distribution detected"
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ # Verify installation folder
360
+ if @config[:app_path]
361
+ log.info "Installation Path: #{@config[:app_path]}"
362
+ if FileTest.exists?(@config[:app_path])
363
+ @config[:app_path].gsub!('\\', '/')
364
+ else
365
+ log.error "No valid installation folder found, please abort!"
366
+ wait
367
+ Process.exit
368
+ end
369
+ else
370
+ log.error "No valid installation folder found, please abort!"
371
+ wait
372
+ Process.exit
373
+ end
374
+
375
+ # Nasty workaround for cygwin on Vista vs XP, vs acl's
376
+ =begin
377
+ @config[:app_path][/\A(\w\:)/]
378
+ str = "#{$1}/ /six-app-root ntfs noacl 0 0"
379
+ etc = File.join(TOOL_PATH, 'etc')
380
+ FileUtils.mkdir_p etc
381
+ File.open(File.join(etc, 'fstab'), 'w') { |file| file.puts str }
382
+ ENV['six-app-root'] = '/six-app-root'
383
+ =end
384
+ puts
385
+ if @config[:server][:repository]
386
+ log.info "Checking for latest updates of #{@config[:server][:name]} server info"
387
+ path = File.join(@config[:server_path], @config[:server][:name])
388
+
389
+ opts = {}
390
+ opts.merge!({:log => log}) if @config[:verbose]
391
+
392
+ case @config[:server][:repository][0]
393
+ when /\Agit:\/\//
394
+ repo = GitRepo.new(@config[:server][:repository], path)
395
+ when /\Arsync:\/\//
396
+ repo = RsyncRepo.new(@config[:server][:repository], path)
397
+ end
398
+
399
+ if FileTest.exist?(File.join(path, '.git')) || FileTest.exist?(File.join(path, '.rsync'))
400
+ repo.reset(:hard => true)
401
+ repo.update
402
+ else
403
+ #FileUtils::mkdir_p path
404
+ repo.clone
405
+ end
406
+
407
+ begin
408
+ srv = File.join(path, 'server.yml')
409
+ File.open(srv) do |f|
410
+ cfg = YAML::load(f)
411
+ @config[:folders].each do |f|
412
+ cfg[:folders].each do |f2|
413
+ @config[:folders] -= [f] if f2[:folder] == f[:folder]
414
+ end
415
+ end
416
+ @config[:folders] = cfg[:folders] + @config[:folders]
417
+ @config[:server] = cfg.merge @config[:server]
418
+ end
419
+ rescue
420
+ log.warn "WARNING: Server configured but unable to get data from repository"
421
+ end
422
+ end
423
+
424
+ @config[:folders].uniq!
425
+ log.debug "@config #{@config}"
426
+
427
+ # PreProcess the config data
428
+ @mods = []
429
+ @mods << @config[:mods] if @config[:mods]
430
+
431
+ @config[:folders].each do |folder|
432
+ folder[:folder].gsub!('\\', '/')
433
+ if folder[:mods]
434
+ @mods << folder[:mods]
435
+ else
436
+ @mods << folder[:folder]
437
+ end
438
+ end
439
+ @mods = @mods.join(';')
440
+
441
+ # Output processed config data
442
+ log.info "Manager for: #{@mods}"
443
+
444
+
445
+ # Prepare stats
446
+ @stats = []
447
+ @config[:folders].each do |folder|
448
+ stat = Hash.new
449
+ @stats << stat
450
+ path = File.join(@config[:app_path], folder[:folder])
451
+ stat[:changelog] = []
452
+ end
453
+ end
454
+ end
455
+ end
456
+
457
+ # TODO: Evaluate position
458
+ if $0 == __FILE__
459
+ end
@@ -0,0 +1,110 @@
1
+ # encoding: UTF-8
2
+
3
+ module Six
4
+ module Updater
5
+ class GitRepo
6
+ def initialize(repositories, path, depth = 1)
7
+ @repositories = repositories
8
+ @path = path
9
+ @depth = depth
10
+ Six::Updater.setup_git
11
+ end
12
+
13
+ def open
14
+ repository
15
+ end
16
+
17
+ def repository
18
+ @repository || Git.open(@path)
19
+ end
20
+
21
+ def log
22
+ Six::Updater.log
23
+ end
24
+
25
+ def reset(opts = {})
26
+ repository.reset(nil, opts)
27
+ end
28
+
29
+ def clone(opts = {})
30
+ repositories = @repositories.clone
31
+ done = false
32
+ opts[:depth] = @depth
33
+ while repositories.size > 0 && !done
34
+ rep = repositories.sample
35
+ repositories -= [rep]
36
+ log.info "Trying #{rep}"
37
+
38
+ begin
39
+ Git.clone(rep, @path, opts)
40
+ Git.open(@path).config("core.autocrlf", "false")
41
+ done = true
42
+ rescue
43
+ log.debug "WARNING: Failed, trying other mirrors if available"
44
+ end
45
+ end
46
+ log.warn "WARNING: Failed, was unable to clone!" unless done
47
+ done
48
+ end
49
+
50
+ def version
51
+ repository.log.first.to_s
52
+ end
53
+
54
+ def update(opts = {})
55
+ done = false
56
+ repositories = @repositories.clone
57
+ while repositories.size > 0 && !done
58
+ cfg = nil
59
+ File.open(File.join(@path, '.git/config')) { |file| cfg = file.readlines }
60
+ rep = ''
61
+ cfg.map! do |entry|
62
+ if entry[/(\t*)url \=/]
63
+ rep = repositories.sample
64
+ repositories -= [rep]
65
+ "#{$1}url = #{rep}"
66
+ else
67
+ entry
68
+ end
69
+ end
70
+ log.info "Trying #{rep}"
71
+ File.open(File.join(@path, '.git/config'), 'w') { |file| file.puts cfg }
72
+ begin
73
+ repository.config("core.autocrlf", "false")
74
+ repository.pull_legacy
75
+ done = true
76
+ rescue
77
+ log.debug "WARNING: Failed, trying other mirrors if available"
78
+ end
79
+ end
80
+ log.warn "WARNING: Failed, was unable to update!" unless done
81
+ done
82
+ end
83
+
84
+ def status(force = false)
85
+ if force || !@status
86
+ @status = status_update
87
+ end
88
+ @status
89
+ end
90
+
91
+ def check_status(type, array, msg)
92
+ str = []
93
+ if array.size > 0
94
+ str << "#{type}:"
95
+ array.each { |item| str << item[0] }
96
+ msg << str.join("\n")
97
+ end
98
+ end
99
+
100
+ def status_update
101
+ msg = []
102
+ [
103
+ ['Added', repository.status.added], ['Changed', repository.status.changed],
104
+ ['Deleted', repository.status.deleted], ['Untracked', repository.status.untracked]
105
+ ].each { |entry| check_status(entry[0], entry[1], msg) }
106
+ msg
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,355 @@
1
+ # encoding: UTF-8
2
+
3
+ module Six
4
+ module Updater
5
+ class Mod
6
+ def initialize(modconfig, config)
7
+ @modconfig = modconfig
8
+ @config = config
9
+ @stat = Hash.new
10
+ @stat[:changelog] = []
11
+ @stat[:current], @stat[:previous] = "A", "B"
12
+ @path = File.join(@config[:app_path], @modconfig[:folder])
13
+ get_repositories
14
+
15
+ @modconfig[:folder].gsub!('\\', '/')
16
+ @folder_str = @modconfig[:folder].clone
17
+ @folder_str.gsub!('/', '-')
18
+
19
+ # TODO: Implement this into the real modfolder so it can be verified by the server you join? - 'exclude version.txt' (.gitignore)
20
+ if @modconfig[:versionfile]
21
+ @modconfig[:versionfile] = File.join(@config[:app_path], @modconfig[:folder], @modconfig[:versionfile])
22
+ else
23
+ @modconfig[:versionfile] = File.join(@config[:app_path], @modconfig[:folder], '.version.txt')
24
+ end
25
+ if @modconfig[:changelog]
26
+ @modconfig[:changelog] = File.join(@config[:app_path], @modconfig[:folder], @modconfig[:changelog])
27
+ else
28
+ @modconfig[:changelog] = File.join(BASE_PATH, 'changelogs', "Changelog_#{@folder_str}.txt")
29
+ end
30
+
31
+ unless @modconfig[:depth]
32
+ @modconfig[:depth] = @config[:depth]
33
+ end
34
+
35
+ @changed = false
36
+
37
+
38
+ # TODO: Handle proper Git class handling for non existent repository / not a git repos
39
+ # TODO: Git.open requires git to be required, which in turn requires a git binary. Should not be required for uninstall for example.
40
+ @installed = if FileTest.exists?(File.join(@path, '.git'))
41
+ # begin
42
+ @repository = GitRepo.new(@modconfig[:repository], @path)
43
+ true
44
+ # rescue
45
+ # log.warn "WARNING: Could not open #{@path}"
46
+ # false
47
+ # end
48
+ elsif FileTest.exists?(File.join(@path, '.rsync'))
49
+ # begin
50
+ @repository = RsyncRepo.new(@modconfig[:repository], @path, :log => log)
51
+ true
52
+ # rescue
53
+ # log.warn "WARNING: Could not open #{@path}"
54
+ # false
55
+ # end
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ def get_repositories
62
+ cfg = File.join(@path, '.repositories.yml')
63
+ @repositories = if FileTest.exist?(cfg)
64
+ File.open(cfg) { |file| YAML::load(file) }
65
+ elsif @modconfig[:repository].class == Array
66
+ @modconfig[:repository]
67
+ else
68
+ [@modconfig[:repository]]
69
+ end
70
+ end
71
+
72
+ def log
73
+ Six::Updater.log
74
+ end
75
+
76
+ def changed
77
+ @changed
78
+ end
79
+
80
+ def sure?
81
+ Six::Updater.sure?
82
+ end
83
+
84
+ def log_status
85
+ log.warn "Status: #{@repository.status.join("\n")}" if @repository.status.size > 0
86
+ end
87
+
88
+ def not_a_repository
89
+ log.warn "Does not seem to be a repository!"
90
+ end
91
+
92
+ def write_version
93
+ @modconfig[:versionfile][/(.*)\/(.*)/]
94
+ folder = $1
95
+ FileUtils.mkdir_p folder unless FileTest.exists? folder
96
+ begin
97
+ File.open(@modconfig[:versionfile], 'w') { |file| file.puts @stat[:current] }
98
+ true
99
+ rescue
100
+ log.warn "WARNING: Unable to write version.txt file, please make sure everything arma is closed, etc"
101
+ false
102
+ end
103
+ end
104
+
105
+ # Process Comment Changelog
106
+ def write_changelog
107
+ str = "#{@stat[:current]}"
108
+ str += " - #{@stat[:previous]}" if @stat[:previous]
109
+ log.info str
110
+
111
+ history = @repository.repository.log(LOG_LIMIT)
112
+ @stat[:changelog] = []
113
+ if @stat[:previous]
114
+ history.between(@stat[:previous], @stat[:current]).each do |c|
115
+ c.message.split("\n").each { |msg| @stat[:changelog] << msg }
116
+ end
117
+ else
118
+ history.each do |c|
119
+ c.message.split("\n").each { |msg| @stat[:changelog] << msg }
120
+ end
121
+ # DONT SORT, otherwise all messed up stuff :)
122
+ #@stat[:changelog].sort!
123
+ end
124
+
125
+ # TODO: Clean empty lines etc
126
+ content = "== #{str}\n"
127
+ content += @stat[:changelog].join("\n")
128
+ content += "\n\n"
129
+ File.open(@modconfig[:changelog], 'r') { |file| content += file.read } if FileTest.exists?(@modconfig[:changelog])
130
+ @modconfig[:changelog][/(.*)\/(.*)/]
131
+ folder = $1
132
+ FileUtils.mkdir_p folder unless FileTest.exists? folder
133
+ File.open(@modconfig[:changelog], 'w') { |file| file.write content }
134
+ end
135
+
136
+ def changelog
137
+ unless @installed
138
+ not_a_repository
139
+ return
140
+ end
141
+
142
+ unless @stat[:previous] == @stat[:current]
143
+ log.info 'Playing log...'
144
+ @stat[:changelog].each do |msg|
145
+ log.debug msg # TODO: Necessary?
146
+ puts msg
147
+ end
148
+ end
149
+ end
150
+
151
+ def process(command)
152
+ # TODO: Exception handling, incase working folder was modified
153
+ log.info "Processing: #{@modconfig[:folder]} (#{command})"
154
+ cwd = Dir.pwd
155
+ if FileTest.exist?(@path)
156
+ Dir.chdir @path
157
+ system "git #{command}"
158
+ Dir.chdir cwd
159
+ else
160
+ log.warn "WARNING: Can't find folder. Folder does not seem to exist"
161
+ end
162
+ end
163
+
164
+ # CLEANUP Process (repack -ad / gc --agressive / --auto)
165
+ # TODO: run with --aggressive every 100 commits or so :O | Evaluate repack -adf f being more aggressive?
166
+ # Is aggressive usable yet? http://metalinguist.wordpress.com/2007/12/06/the-woes-of-git-gc-aggressive-and-how-git-deltas-work/
167
+ def cleanup
168
+ unless @installed
169
+ not_a_repository
170
+ return
171
+ end
172
+ unless @stat[:previous] == @stat[:current]
173
+ log.info 'Cleaning...'
174
+ if @modconfig[:repository][0][/\Agit:\/\//]
175
+ @repository.repository.gc_legacy
176
+ @repository.repository.repack
177
+ end
178
+ end
179
+ end
180
+
181
+ # RESET process (incase everything FOOKED :D)
182
+ def reset
183
+ unless @installed
184
+ not_a_repository
185
+ return
186
+ end
187
+ log.warn "###########"
188
+ log.warn "# WARNING #"
189
+ log.warn "###########"
190
+ log.warn "You are about to reset and loose all local changes in the specified repository"
191
+ log_status
192
+ sure?
193
+ log.info 'Resetting...'
194
+ @repository.repository.reset(nil, {:hard => true})
195
+ end
196
+
197
+ # UPDATE Process
198
+ def update
199
+ unless @installed
200
+ not_a_repository
201
+ return
202
+ end
203
+
204
+ log.info 'Updating... This might take a while, please wait.'
205
+ @stat[:previous] = @repository.version
206
+ done = @repository.update
207
+ @stat[:current] = @repository.version
208
+
209
+ begin
210
+ @repository.status(true)
211
+ rescue
212
+ log.warn 'WARNING: Update seems to have completed but there was a status error..'
213
+ log_status
214
+ end
215
+
216
+ if @stat[:previous] == @stat[:current]
217
+ log.info "No updates found"
218
+ write_version # DO this always incase previous version.txt failed for whatever reason!
219
+ else
220
+ @changed = true
221
+ if write_version
222
+ #write_changelog
223
+ else
224
+ @repository.reset(@stat[:previous], {:hard => true})
225
+ @changed = false
226
+ @stat[:current] = @stat[:previous]
227
+ end
228
+ end
229
+
230
+ unless done
231
+ log.warn 'WARNING: Update was unable to complete. This could be due to a server / connection problem, or due to files in use or out of synch repositories. Please make sure ArmA is closed, retry, otherwise reset and retry'
232
+ log_status
233
+ # TODO: Add display of stats for changed files etc
234
+ end
235
+ end
236
+
237
+ # INSTALL process
238
+ def install
239
+ log.debug "Path: #{@path}"
240
+ if @installed
241
+ log.info "Already installed, trying to update..."
242
+ update
243
+ else
244
+ if FileTest.exist?(@path)
245
+ log.info "WARNING: Folder already exists but does not seem to be a repository. Not installing"
246
+ else
247
+ log.info "Installing (depth: #{@modconfig[:depth]})... This might take a while, please wait."
248
+ folder = ''
249
+ if @modconfig[:folder][/(.*)\/(.*)/]
250
+ basepath, subfolder = $1, $2
251
+ basepath = File.join(@config[:app_path], basepath)
252
+ FileUtils.mkdir_p basepath unless FileTest.exist?(basepath)
253
+ #Dir.chdir basepath
254
+ #folder = File.join(basepath, subfolder)
255
+ end
256
+
257
+ file = File.join(BASE_PATH, "#{@folder_str}.7z")
258
+ if FileTest.exist?(file)
259
+ log.info "Found #{@folder_str}.7z, using it"
260
+ Six::Updater.zipcheck
261
+ FileUtils.mkdir_p @path
262
+ cmd = "7z x -o\"#{@path}\" \"#{file}\""
263
+ log.debug cmd
264
+ output = %x[#{cmd}]
265
+ log.debug output
266
+ begin
267
+ open
268
+ @repository.reset(nil, {:hard => true})
269
+ @installed = true
270
+ get_repositories
271
+ update
272
+ rescue
273
+ log.warn "WARNING: Failed to open the repository, not installed!"
274
+ end
275
+ else
276
+ @repository = if @repositories[0][/\Arsync:\/\//]
277
+ RsyncRepo.new(@repositories, @path, @modconfig[:depth])
278
+ else
279
+ GitRepo.new(@repositories, @path, @modconfig[:depth])
280
+ end
281
+ opts = {}
282
+ opts.merge!({:log => log}) if @config[:verbose]
283
+ @installed = @repository.clone(opts)
284
+ unless @installed
285
+ log.warn "WARNING: Failed to download the repository! This could be due to a server / connection problem. Please try again later"
286
+ end
287
+ end
288
+
289
+ if @installed
290
+ @changed = true
291
+ @stat[:previous] = nil
292
+ begin
293
+ @stat[:current] = @repository.version
294
+ if @modconfig[:repository][0][/git:\/\//]
295
+ @repository.status(true)
296
+ FileUtils.rm_f @modconfig[:changelog] if FileTest.exists?(@modconfig[:changelog])
297
+ end
298
+ write_version
299
+ #write_changelog
300
+ rescue
301
+ log.warn 'WARNING: Post installation was unable to complete. This could be due to a server / connection problem. Please try again later'
302
+ FileUtils::rm_rf(@path) if FileTest.exist?(@path)
303
+ end
304
+ begin
305
+ userconfig
306
+ rescue
307
+ log.warn 'WARNING: Userconfig processing is unable to complete.'
308
+ end
309
+ else
310
+ FileUtils::rm_rf(@path) if FileTest.exist?(@path)
311
+ end
312
+ end
313
+ end
314
+ end
315
+
316
+ # UNINSTALL process
317
+ def uninstall
318
+ if FileTest.exist?(@path)
319
+ log.warn "###########"
320
+ log.warn "# WARNING #"
321
+ log.warn "###########"
322
+ log.warn "You are about to uninstall the specified repository"
323
+ sure?
324
+ log.warn 'Uninstalling...'
325
+ FileUtils.rm_rf @path
326
+ FileUtils.rm_f @modconfig[:changelog] if FileTest.exists?(@modconfig[:changelog])
327
+ FileUtils.rm_f @modconfig[:versionfile] if FileTest.exists?(@modconfig[:versionfile])
328
+ end
329
+ # TODO: Cleanup Userconfig ?
330
+ end
331
+
332
+ # Process userconfigs
333
+ # TODO: Add this processing to update, aka; IF userconfig has changed THEN update it
334
+ def userconfig
335
+ unless @installed
336
+ not_a_repository
337
+ return
338
+ end
339
+ # TODO: Process this on file level instead of folder level?
340
+ time = Time.now.to_i
341
+ f = @modconfig[:folder].clone
342
+ f.gsub!('@', '')
343
+ path = File.join(@path, 'userconfig')
344
+ uconfig = File.join(@config[:app_path], 'userconfig')
345
+ uconfigpath = File.join(uconfig, f)
346
+ if FileTest.exist?(path)
347
+ log.info "Found userconfig, processing..."
348
+ FileUtils::mv(uconfigpath, "#{uconfigpath}_#{time}") if FileTest.exist?(uconfigpath)
349
+ FileUtils::mkdir_p uconfig unless FileTest.exist?(uconfig)
350
+ FileUtils::cp_r(path, uconfigpath)
351
+ end
352
+ end
353
+ end
354
+ end
355
+ end
@@ -0,0 +1,130 @@
1
+ # encoding: UTF-8
2
+
3
+ # Docu: http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
4
+ require 'optparse'
5
+
6
+ module Six
7
+ module Updater
8
+ module_function
9
+ def parse_options
10
+ todo, general_todo, second_todo = [], [], []
11
+
12
+ options = Hash.new
13
+ OptionParser.new do |opts|
14
+ $0[/.*\/(.*)/]
15
+ opts.banner = "Usage: #{$1} [config.yml] [options]"
16
+
17
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
18
+ options[:verbose] = v
19
+ end
20
+
21
+ opts.on("-i", "--install", "Installs the mods, unless already installed") do |bool|
22
+ todo << :install if bool
23
+ end
24
+
25
+ opts.on("-d", "--uninstall", "Uninstalls the mods, unless not installed") do |bool|
26
+ todo << :uninstall if bool
27
+ end
28
+
29
+ opts.on("-u", "--update", "Updates each mod to latest version") do |bool|
30
+ todo << :update if bool
31
+ end
32
+
33
+ opts.on("-r", "--reset", "Resets the modfolders to latest commit status. All modifications lost") do |bool|
34
+ todo << :reset if bool
35
+ end
36
+
37
+ opts.on("-z", "--userconfig", "Processes the userconfigs of each mod. Makes backups of previous files") do |bool|
38
+ todo << :userconfig if bool
39
+ end
40
+
41
+ opts.on("-l", "--changelog", "Display Changelog after update") do |bool|
42
+ second_todo << :changelog if bool
43
+ end
44
+
45
+ opts.on("-c", "--cleanup", "Cleans up the modfolders git repositories") do |bool|
46
+ second_todo << :cleanup if bool
47
+ end
48
+
49
+ opts.on("-s", "--createshortcut", "Creates shortcut to run the game with the mods, in installation folder") do |bool|
50
+ general_todo << :createshortcut if bool
51
+ end
52
+
53
+ opts.on("-k", "--createdesktopshortcut", "Creates shortcut to run the game with the mods, on the desktop") do |bool|
54
+ general_todo << :createdesktopshortcut if bool
55
+ end
56
+
57
+ opts.on("-j", "--join", "Starts the game with #{@config[:app_params]} -mod=#{@mods} -connect=#{@config[:server][:address]} -port=#{@config[:server][:port]}") do |bool|
58
+ general_todo << :joingame if bool
59
+ end
60
+
61
+ opts.on("-g", "--startgame", "Starts the game with #{@config[:app_params]} -mod=#{@mods}") do |bool|
62
+ general_todo << :startgame if bool
63
+ end
64
+
65
+ opts.on("-w", "--wait", "Waits at the end for user pressing ENTER") do |bool|
66
+ options[:wait] = bool
67
+ end
68
+
69
+ opts.on("-f", "--force", "Forces to run tasks even though no changes were detected") do |bool|
70
+ options[:force] = bool
71
+ end
72
+
73
+ opts.on("--depth I", Integer, "Clone depth, default: #{@config[:depth]}. Set to 0 to clone all history") do |s|
74
+ options[:depth] = s
75
+ end
76
+
77
+ opts.on("--mods S", String, "Additional Mods") do |s|
78
+ options[:mods] = s
79
+ end
80
+
81
+ opts.on("--armapath S", String, "Destination folder") do |s|
82
+ options[:armapath] = s
83
+ end
84
+
85
+ opts.on("--gitpath S", String, "Git binary") do |s|
86
+ options[:gitpath] = s
87
+ end
88
+ end.parse!
89
+
90
+ default = if (todo + second_todo + general_todo).size > 0
91
+ false
92
+ else
93
+ true
94
+ end
95
+
96
+ # TODO: Move this to Updater ?
97
+ @todo = if todo.size > 0
98
+ todo
99
+ else
100
+ log.info "No parameters given, running the default"
101
+ #options[:wait] = true
102
+ if default
103
+ @config[:defaultactions]
104
+ else
105
+ []
106
+ end
107
+ end
108
+ @general_todo = if general_todo.size > 0
109
+ general_todo
110
+ else
111
+ if default
112
+ @config[:defaultgeneralactions]
113
+ else
114
+ []
115
+ end
116
+ end
117
+
118
+ @second_todo = if second_todo.size > 0
119
+ second_todo
120
+ else
121
+ if default
122
+ @config[:defaultsecondactions]
123
+ else
124
+ []
125
+ end
126
+ end
127
+ @config = @config.merge(options)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: UTF-8
2
+
3
+ module Six
4
+ module Updater
5
+ class RsyncRepo
6
+ def initialize(repositories, path, depth = 1)
7
+ @repositories = repositories
8
+ @path = path
9
+ @changed = false
10
+ @installed = false
11
+ end
12
+
13
+ def log
14
+ Six::Updater.log
15
+ end
16
+
17
+ def version
18
+ f = File.join(@path, '.rsync', '.pack', '.repository.yml')
19
+ if FileTest.exist?(f)
20
+ File.open(f) {|file| YAML::load(file)[:version] }
21
+ else
22
+ nil
23
+ end
24
+ end
25
+
26
+ def open(opts = {})
27
+ repository(opts)
28
+ end
29
+
30
+ def reset(opts = {})
31
+ repository.reset(opts)
32
+ end
33
+
34
+ def status(force = false)
35
+ []
36
+ end
37
+
38
+ def repository(opts = {:log => log})
39
+ @repository || Six::Repositories::Rsync.open(@path, opts)
40
+ end
41
+
42
+ def clone(opts = {})
43
+ done = false
44
+ @path[/(.*)\/(.*)/]
45
+ opts = {:path => $1, :depth => @depth, :log => log}
46
+ #begin
47
+ @repository = Rsync.clone(@repositories, $2, opts)
48
+ # FIXME: Update needs to be fixed
49
+ done = true
50
+ # rescue
51
+ # log.warn "WARNING: Failed, was unable to clone!" unless done
52
+ #log.debug "WARNING: Failed, trying other mirrors if available"
53
+ # end
54
+ done
55
+ end
56
+
57
+ def update(opts = {})
58
+ cfg = nil
59
+ File.open(File.join(@path, '.rsync/config.yml')) { |file| cfg = YAML::load(file) }
60
+ cfg[:hosts] = @repositories
61
+ File.open(File.join(@path, '.rsync/config.yml'), 'w') { |file| file.puts cfg.to_yaml }
62
+
63
+ # begin
64
+ repository.update
65
+ done = true
66
+ # rescue
67
+ # log.warn "WARNING: Failed, was unable to update!" unless done
68
+ # end
69
+ done
70
+ end
71
+ end
72
+ end
73
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: six-updater
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.1
5
+ platform: ruby
6
+ authors:
7
+ - Sickboy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-25 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: log4r
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: six-rsync
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: win32-process
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.6.0
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: win32-shortcut
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.2.3
54
+ version:
55
+ description: Your summary here
56
+ email: sb@dev-heaven.net
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README
63
+ - LICENSE
64
+ files:
65
+ - LICENSE
66
+ - README
67
+ - Rakefile
68
+ - lib/six/updater/gitrepo.rb
69
+ - lib/six/updater/mod.rb
70
+ - lib/six/updater/options.rb
71
+ - lib/six/updater/rsyncrepo.rb
72
+ - lib/six/updater-app.rb
73
+ - lib/six/updater.rb
74
+ has_rdoc: true
75
+ homepage:
76
+ licenses: []
77
+
78
+ post_install_message:
79
+ rdoc_options: []
80
+
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: "0"
94
+ version:
95
+ requirements: []
96
+
97
+ rubyforge_project:
98
+ rubygems_version: 1.3.5
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: Your summary here
102
+ test_files: []
103
+