six-updater 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
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
+