ratch 0.4.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. data/COPYING +17 -669
  2. data/HISTORY +6 -0
  3. data/MANIFEST +36 -0
  4. data/METADATA +14 -0
  5. data/NEWS +7 -0
  6. data/README +67 -17
  7. data/bin/ratch +5 -78
  8. data/demo/tryme-task.ratch +12 -0
  9. data/demo/tryme1.ratch +6 -0
  10. data/lib/ratch/core_ext.rb +6 -0
  11. data/lib/ratch/core_ext/facets.rb +1 -0
  12. data/lib/ratch/core_ext/filetest.rb +52 -0
  13. data/lib/ratch/core_ext/object.rb +8 -0
  14. data/lib/ratch/core_ext/pathname.rb +38 -0
  15. data/lib/ratch/core_ext/string.rb +44 -0
  16. data/lib/ratch/{dsl/console.rb → core_ext/to_console.rb} +2 -76
  17. data/lib/ratch/core_ext/to_list.rb +29 -0
  18. data/lib/ratch/dsl.rb +494 -49
  19. data/lib/ratch/index.rb +4 -0
  20. data/lib/ratch/io.rb +116 -0
  21. data/lib/ratch/pathglob.rb +73 -0
  22. data/lib/ratch/plugin.rb +55 -0
  23. data/lib/ratch/runmode.rb +69 -0
  24. data/lib/ratch/script.rb +52 -0
  25. data/lib/ratch/service.rb +33 -0
  26. data/lib/ratch/task.rb +249 -0
  27. data/lib/ratch/task2.rb +298 -0
  28. data/test/README +1 -0
  29. data/test/test_helper.rb +4 -0
  30. data/test/test_task.rb +46 -0
  31. metadata +90 -150
  32. data/CHANGES +0 -22
  33. data/TODO +0 -2
  34. data/bin/lt +0 -56
  35. data/bin/ludo +0 -14
  36. data/bin/manifest +0 -451
  37. data/bin/ratch-find +0 -21
  38. data/demo/WILMA +0 -1
  39. data/demo/XR +0 -9
  40. data/demo/lib/foo/foo.rb +0 -7
  41. data/demo/p.rb +0 -9
  42. data/demo/r.rb +0 -6
  43. data/demo/t.rb +0 -3
  44. data/demo/task/config.yaml +0 -4
  45. data/demo/task/one +0 -6
  46. data/demo/task/simplebuild +0 -15
  47. data/demo/task/stats +0 -4
  48. data/demo/task/task +0 -6
  49. data/demo/task/tryme +0 -10
  50. data/lib/ratch/dsl/argv.rb +0 -112
  51. data/lib/ratch/dsl/batch.rb +0 -232
  52. data/lib/ratch/dsl/build.rb +0 -174
  53. data/lib/ratch/dsl/email.rb +0 -108
  54. data/lib/ratch/dsl/file.rb +0 -205
  55. data/lib/ratch/dsl/meta.rb +0 -125
  56. data/lib/ratch/dsl/options.rb +0 -98
  57. data/lib/ratch/dsl/setup.rb +0 -124
  58. data/lib/ratch/dsl/sign.rb +0 -243
  59. data/lib/ratch/dsl/stage.rb +0 -147
  60. data/lib/ratch/dsl/task.rb +0 -139
  61. data/lib/ratch/dsl/upload.rb +0 -436
  62. data/lib/ratch/dsl/zip.rb +0 -59
  63. data/lib/ratch/extra/email.rb +0 -5
  64. data/lib/ratch/extra/stage.rb +0 -5
  65. data/lib/ratch/extra/zip.rb +0 -5
  66. data/lib/ratch/manager.rb +0 -53
  67. data/lib/ratch/manifest.rb +0 -540
  68. data/lib/ratch/metadata/information.rb +0 -258
  69. data/lib/ratch/metadata/package.rb +0 -108
  70. data/lib/ratch/metadata/project.rb +0 -523
  71. data/lib/ratch/metadata/release.rb +0 -108
  72. data/lib/ratch/support/errors.rb +0 -4
  73. data/lib/ratch/support/filename.rb +0 -18
  74. data/lib/ratch/support/filetest.rb +0 -29
  75. data/lib/ratch/toolset/ruby/announce +0 -224
  76. data/lib/ratch/toolset/ruby/compile +0 -49
  77. data/lib/ratch/toolset/ruby/install +0 -77
  78. data/lib/ratch/toolset/ruby/notes +0 -185
  79. data/lib/ratch/toolset/ruby/pack/gem +0 -93
  80. data/lib/ratch/toolset/ruby/pack/tgz +0 -46
  81. data/lib/ratch/toolset/ruby/pack/zip +0 -46
  82. data/lib/ratch/toolset/ruby/publish +0 -57
  83. data/lib/ratch/toolset/ruby/release +0 -8
  84. data/lib/ratch/toolset/ruby/setup +0 -1616
  85. data/lib/ratch/toolset/ruby/stamp +0 -33
  86. data/lib/ratch/toolset/ruby/stats +0 -138
  87. data/lib/ratch/toolset/ruby/test/crosstest +0 -305
  88. data/lib/ratch/toolset/ruby/test/extest +0 -129
  89. data/lib/ratch/toolset/ruby/test/isotest +0 -293
  90. data/lib/ratch/toolset/ruby/test/load +0 -39
  91. data/lib/ratch/toolset/ruby/test/loadtest +0 -28
  92. data/lib/ratch/toolset/ruby/test/syntax +0 -29
  93. data/lib/ratch/toolset/ruby/test/test +0 -26
  94. data/lib/ratch/toolset/sandbox/query +0 -11
  95. data/man/ratch.man +0 -73
  96. data/meta/MANIFEST +0 -130
  97. data/meta/config.yaml +0 -9
  98. data/meta/icli.yaml +0 -16
  99. data/meta/project.yaml +0 -20
  100. data/meta/ratch.roll +0 -2
  101. data/meta/xProjectInfo +0 -41
  102. data/task/clobber/package +0 -10
  103. data/task/man +0 -14
  104. data/task/publish +0 -57
  105. data/task/release +0 -9
  106. data/task/setup +0 -1616
  107. data/task/stats +0 -138
@@ -0,0 +1,29 @@
1
+ class Array
2
+
3
+ def to_list
4
+ self
5
+ end
6
+
7
+ end
8
+
9
+ class NilClass
10
+
11
+ def to_list
12
+ []
13
+ end
14
+
15
+ end
16
+
17
+ class String
18
+
19
+ # Helper method for cleaning list options.
20
+ # This will split the option on ':' or ';'
21
+ # if it is a string, rather than an array.
22
+ # And it will make sure there are no nil elements.
23
+
24
+ def to_list
25
+ split(/[:;,\n]/)
26
+ end
27
+
28
+ end
29
+
@@ -1,52 +1,497 @@
1
- # = TITLE:
2
- #
3
- # Main DSL Loader
4
- #
5
- # = COPYING:
6
- #
7
- # Copyright (c) 2007 Psi T Corp.
8
- #
9
- # This file is part of the ProUtils' Ratch program.
10
- #
11
- # Ratch is free software: you can redistribute it and/or modify
12
- # it under the terms of the GNU General Public License as published by
13
- # the Free Software Foundation, either version 3 of the License, or
14
- # (at your option) any later version.
15
- #
16
- # Ratch is distributed in the hope that it will be useful,
17
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- # GNU General Public License for more details.
1
+ require 'yaml'
2
+ require 'rbconfig' # replace with facets/rbsystem?
3
+ require 'fileutils'
4
+
5
+ require 'ratch/core_ext'
6
+ require 'ratch/index'
7
+ require 'ratch/io'
8
+ require 'ratch/runmode'
9
+
10
+ require 'ratch/task'
11
+
12
+ require 'facets/platform'
13
+ require 'facets/arguments'
14
+ require 'facets/ziputils'
15
+
16
+ #require 'annotatable'
17
+ #require 'facets/openhash'
18
+ #require 'facets/argvector'
19
+
20
+ begin
21
+ require 'facets/net/smtp_tls'
22
+ rescue LoadError
23
+ require 'net/smtp'
24
+ end
25
+
26
+
27
+ module Ratch
28
+
29
+ # = Ratch DSL
30
+ #
31
+ # The DSL class is the heart of Ratch, it provides all the convenece methods
32
+ # that make Ratch so convenient for writing Ruby-based batch script.
33
+ #
34
+ class DSL < Module
35
+
36
+ #
37
+ def initialize(ioc={})
38
+ include Taskable
39
+ include Taskable::Dsl
40
+
41
+ extend self
42
+
43
+ @runmode = ioc[:mode] || Runmode.load_argv!
44
+ @io = ioc[:io] || IO.new(@runmode)
45
+ end
46
+
47
+ # Delgate run mode settings to Ratch::RunMode object.
48
+ def runmode
49
+ @runmode
50
+ end
51
+
52
+ # Delgate input/output routines to Ratch::IO object.
53
+ def io
54
+ @io
55
+ end
56
+
57
+ # Delegate file system routines to FileUtils or FileUtils::DryRun,
58
+ # depending on dryrun mode.
59
+ def fileutils
60
+ dryrun? ? ::FileUtils::DryRun : ::FileUtils
61
+ end
62
+
63
+ # Add FileUtils Features
64
+ ::FileUtils.private_instance_methods(false).each do |meth|
65
+ next if meth =~ /^fu_/
66
+ module_eval %{
67
+ def #{meth}(*a,&b)
68
+ fileutils.#{meth}(*a,&b)
69
+ end
70
+ }
71
+ end
72
+
73
+ # Add FileTest Features
74
+ ::FileTest.private_instance_methods(false).each do |meth|
75
+ next if meth =~ /^fu_/
76
+ module_eval %{
77
+ def #{meth}(*a,&b)
78
+ FileTest.#{meth}(*a,&b)
79
+ end
80
+ }
81
+ end
82
+
83
+ #attr_writer :noharm
84
+ #attr_writer :force
85
+ #attr_writer :trace
86
+ #attr_writer :debug
87
+ #attr_writer :quiet
88
+ #alias_method :dryrun=, :noharm=
89
+
90
+ def force? ; runmode.force? ; end
91
+ def trace? ; runmode.trace? ; end
92
+ def debug? ; runmode.debug? ; end
93
+ def noharm? ; runmode.noharm? ; end
94
+ def dryrun? ; runmode.dryrun? ; end
95
+ def quiet? ; runmode.quiet? ; end
96
+ def verbose? ; runmode.verbose? ; end
97
+
98
+ # Current platform.
99
+ def current_platform
100
+ Platform.local.to_s
101
+ end
102
+
103
+ # Load configuration data from a file.
104
+ # Reesults are cached and and empty Hash is
105
+ # returned if the file is not found.
106
+ #
107
+ # Since they are YAML files, they can optionally
108
+ # end with '.yaml' or '.yml'.
109
+ def configuration(file)
110
+ @configuration ||= {}
111
+ @configuration[file] ||= (
112
+ begin
113
+ configuration!(file)
114
+ rescue LoadError
115
+ Hash.new{ |h,k| h[k] = {} }
116
+ end
117
+ )
118
+ end
119
+
120
+ # Load configuration data from a file.
121
+ # The "bang" version will raise an error
122
+ # if file is not found. It also does not
123
+ # cache the results.
124
+ #
125
+ # Since they are YAML files, they can optionally
126
+ # end with '.yaml' or '.yml'.
127
+ def configuration!(file)
128
+ @configuration ||= {}
129
+ patt = file + "{.yml,.yaml,}"
130
+ path = Dir.glob(patt, File::FNM_CASEFOLD).find{ |f| File.file?(f) }
131
+ if path
132
+ # The || {} is in case the file is empty.
133
+ data = YAML::load(File.open(path)) || {}
134
+ @configuration[file] = data
135
+ else
136
+ raise LoadError, "Missing file -- #{path}"
137
+ end
138
+ end
139
+
140
+ # Shell runner.
141
+ def shell(cmd)
142
+ if dryrun?
143
+ puts cmd
144
+ true
145
+ else
146
+ puts "--> system call: #{cmd}" if trace?
147
+ if quiet?
148
+ silently{ system(cmd) }
149
+ else
150
+ system(cmd)
151
+ end
152
+ end
153
+ end
154
+
155
+ # TODO: DEPRECATE #sh in favor of #shell.
156
+ alias_method :sh, :shell
157
+
158
+ #
159
+ def commandline
160
+ #@commandline ||= ArgVector.new(ARGV)
161
+ @commandline ||= CLI::Arguments.new #(ARGV)
162
+ end
163
+
164
+ # Duplicate of ARGV.
165
+ #def argv
166
+ # @argv ||= ARGV.dup
167
+ #end
168
+
169
+
170
+ # Convert command line argv to args.
171
+ #
172
+ # TODO Is this implmented as expected?
173
+ #def command_parameters
174
+ # ARGV.to_params
175
+ #end
176
+
177
+ # Internal status report.
178
+ # Only output if dryrun or trace mode.
179
+ def status(message)
180
+ io.status(message)
181
+ end
182
+
183
+ # Convenient method to get simple console reply.
184
+ def ask(question, answers=nil)
185
+ io.ask(question, answers)
186
+ end
187
+
188
+ # Ask for a password. (FIXME: only for unix so far)
189
+ def password(prompt=nil)
190
+ io.password(prompt)
191
+ end
192
+
193
+ # Provides convenient starting points in the file system.
194
+ #
195
+ # root #=> #<Pathname:/>
196
+ # home #=> #<Pathname:/home/jimmy>
197
+ # work #=> #<Pathname:/home/jimmy/Documents>
198
+ #
199
+
200
+ # Current root path.
201
+ def root(*args)
202
+ Pathname['/', *args]
203
+ end
204
+
205
+ # Current home path.
206
+ def home(*args)
207
+ Pathname['~', *args].expand_path
208
+ end
209
+
210
+ # Current working path.
211
+ def work(*args)
212
+ Pathname['.', *args]
213
+ end
214
+
215
+ alias_method :pwd, :work
216
+
217
+ # Bonus FileUtils features.
218
+ def cd(*a,&b)
219
+ puts "cd #{a}" if dryrun? or trace?
220
+ fileutils.chdir(*a,&b)
221
+ end
222
+
223
+ # Read file.
224
+ def file_read(path)
225
+ File.read(path)
226
+ end
227
+
228
+ # Write file.
229
+ def file_write(path, text)
230
+ if dryrun?
231
+ puts "write #{path}"
232
+ else
233
+ File.open(path, 'w'){ |f| f << text }
234
+ end
235
+ end
236
+
237
+ # Assert that a path exists.
238
+ def exists?(path)
239
+ paths = Dir.glob(path)
240
+ paths.not_empty?
241
+ end
242
+ alias_method :exist?, :exists? #; module_function :exist?
243
+ alias_method :path?, :exists? #; module_function :path?
244
+
245
+ # Assert that a path exists.
246
+ def exists!(*paths)
247
+ abort "path not found #{path}" unless paths.any?{|path| exists?(path)}
248
+ end
249
+ alias_method :exist!, :exists! #; module_function :exist!
250
+ alias_method :path!, :exists! #; module_function :path!
251
+
252
+ # Is a given path a regular file? If +path+ is a glob
253
+ # then checks to see if all matches are refular files.
254
+ def file?(path)
255
+ paths = Dir.glob(path)
256
+ paths.not_empty? && paths.all?{ |f| FileTest.file?(f) }
257
+ end
258
+
259
+ # Assert that a given path is a file.
260
+ def file!(*paths)
261
+ abort "file not found #{path}" unless paths.any?{|path| file?(path)}
262
+ end
263
+
264
+ # Is a given path a directory? If +path+ is a glob
265
+ # checks to see if all matches are directories.
266
+ def dir?(path)
267
+ paths = Dir.glob(path)
268
+ paths.not_empty? && paths.all?{ |f| FileTest.directory?(f) }
269
+ end
270
+ alias_method :directory?, :dir? #; module_function :directory?
271
+
272
+ # Assert that a given path is a directory.
273
+ def dir!(*paths)
274
+ paths.each do |path|
275
+ abort "Directory not found: '#{path}'." unless dir?(path)
276
+ end
277
+ end
278
+ alias_method :directory!, :dir! #; module_function :directory!
279
+
280
+ # # Is a file a task?
20
281
  #
21
- # You should have received a copy of the GNU General Public License
22
- # along with Ratch. If not, see <http://www.gnu.org/licenses/>.
23
-
24
- require 'yaml' # make it generally available
25
- require 'rbconfig' # replace with facets/system?
26
-
27
- require 'ratch/dsl/options'
28
- require 'ratch/dsl/batch'
29
- require 'ratch/dsl/build'
30
- require 'ratch/dsl/task'
31
- require 'ratch/dsl/console'
32
- require 'ratch/dsl/file'
33
- require 'ratch/dsl/argv'
34
- require 'ratch/dsl/meta'
35
-
36
- # Load ratch dsl functionality into toplevel.
37
- # This loads into the toplevel's metaclass b/c we don't want
38
- # all these utilies to invade every object [do we?]
39
-
40
- class << self
41
- include Ratch::Dsl::Options
42
- include Ratch::Dsl::Batch
43
- include Ratch::Dsl::Builds
44
- include Ratch::Dsl::Tasks
45
- include Ratch::Dsl::Filing
46
- include Ratch::Dsl::Console
47
- include Ratch::Dsl::Argv
48
- include Ratch::Dsl::Meta
282
+ # def task?(path)
283
+ # task = File.dirname($0) + "/#{path}"
284
+ # task.chomp!('!')
285
+ # task if FileTest.file?(task) && FileTest.executable?(task)
286
+ # end
287
+
288
+ =begin
289
+ # Does a path need updating, based on given +sources+?
290
+ # This compares mtimes of give paths. Returns false
291
+ # if the path needs to be updated.
292
+ #
293
+ # TODO: Put this in FileTest instead?
294
+
295
+ def out_of_date?(path, *sources)
296
+ return true unless File.exist?(path)
297
+
298
+ sources = sources.collect{ |source| Dir.glob(source) }.flatten
299
+ mtimes = sources.collect{ |file| File.mtime(file) }
300
+
301
+ return true if mtimes.empty? # TODO: This the way to go here?
302
+
303
+ File.mtime(path) < mtimes.max
304
+ end
305
+ =end
306
+
307
+ # Glob files.
308
+ def glob(*args, &blk)
309
+ Dir.glob(*args, &blk)
310
+ end
311
+
312
+ # Multiglob files.
313
+ def multiglob(*args, &blk)
314
+ Dir.multiglob(*args, &blk)
315
+ end
316
+
317
+ # Multiglob recursive.
318
+ def multiglob_r(*args, &blk)
319
+ Dir.multiglob_r(*args, &blk)
320
+ end
321
+
322
+ # Stage by hard linking included files to a stage directory.
323
+ #
324
+ # stage_directory Stage directory.
325
+ # files Files to link to stage.
326
+ #
327
+ # TODO: Rename to linkstage or something less likely to name clash?
328
+
329
+ def stage(stage_directory, files)
330
+ return stage_directory if dryrun? # Don't link to stage if dryrun.
331
+
332
+ if File.directory?(stage_directory) # Ensure existance of staging area
333
+ #raise(OverwriteError, stage_directory) unless force?
334
+ rm_r(stage_directory)
335
+ end
336
+
337
+ mkdir_p(stage_directory) #dir = File.expand_path(stage)
338
+
339
+ #files = package.filelist #+ [package.manifest_file]
340
+
341
+ # TODO Dryrun test here or before folder creation?
342
+ files.each do |f| # Link files into staging area.
343
+ file = File.join(stage_directory, f)
344
+ if File.directory?(f)
345
+ mkdir_p(file)
346
+ else
347
+ unless File.exist?(file) and File.mtime(file) >= File.mtime(f)
348
+ ln(f, file) #safe_ln ?
349
+ end
350
+ end
351
+ end
352
+
353
+ return stage_directory
354
+ end
355
+
356
+ # Delegate access to ZipUtils.
357
+ #
358
+ def ziputils
359
+ dryrun? ? ::ZipUtils::DryRun : ::ZipUtils
360
+ end
361
+
362
+ # Compress directory.
363
+
364
+ def compress(format, folder, file=nil, options={})
365
+ case format.to_s.downcase
366
+ when 'zip'
367
+ ziputils.zip(folder, file, options)
368
+ when 'tgz'
369
+ ziputils.tgz(folder, file, options)
370
+ when 'tbz', 'bzip'
371
+ ziputils.tar_bzip(folder, file, options)
372
+ else
373
+ raise ArguementError, "unsupported compression format -- #{format}"
374
+ end
375
+ end
376
+
377
+ # Create a zip file of a directory.
378
+ #
379
+ def zip(folder, file=nil, options={})
380
+ ziputils.zip(folder, file, options)
381
+ end
382
+
383
+ # Create a tar.bz2 file of a directory.
384
+ #
385
+ def tar_bzip(folder, file=nil, options={})
386
+ ziputils.tar_bzip(folder, file, options)
387
+ end
388
+
389
+ # Create a tgz file of a directory.
390
+ #
391
+ def tar_gzip(folder, file=nil, options={})
392
+ ziputils.tar_gzip(folder, file, options)
393
+ end
394
+ alias_method :tgz, :tar_gzip
395
+
396
+ #
397
+ #
398
+ def naming_policy(*policies)
399
+ if policies.empty?
400
+ @naming_policy ||= ['down', 'ext']
401
+ else
402
+ @naming_policy = policies
403
+ end
404
+ end
405
+
406
+ #
407
+ #
408
+ def apply_naming_policy(name, ext)
409
+ naming_policy.each do |policy|
410
+ case policy
411
+ when /^low/, /^down/
412
+ name = name.downcase
413
+ when /^up/
414
+ name = name.upcase
415
+ when /^cap/
416
+ name = name.capitalize
417
+ when /^ext/
418
+ name = name + ".#{ext}"
419
+ end
420
+ end
421
+ name
422
+ end
423
+
424
+ # Email function to easily send out an email.
425
+ #
426
+ # Settings:
427
+ #
428
+ # subject Subject of email message.
429
+ # from Message FROM address [email].
430
+ # to Email address to send announcemnt.
431
+ # server Email server to route message.
432
+ # port Email server's port.
433
+ # domain Email server's domain name.
434
+ # account Email account name if needed.
435
+ # password Password for login..
436
+ # login Login type: plain, cram_md5 or login [plain].
437
+ # secure Uses TLS security, true or false? [false]
438
+ # message Mesage to send -or-
439
+ # file File that contains message.
440
+ #
441
+ def email(message, settings)
442
+ settings ||= {}
443
+ settings.rekey
444
+
445
+ server = settings[:server]
446
+ account = settings[:account] || ENV['EMAIL_ACCOUNT']
447
+ passwd = settings[:password] || ENV['EMAIL_PASSWORD']
448
+ login = settings[:login].to_sym
449
+ subject = settings[:subject]
450
+ mail_to = settings[:to] || settings[:mail_to]
451
+ mail_from = settings[:from] || settings[:mail_from]
452
+ secure = settings[:secure]
453
+ domain = settings[:domain] || server
454
+
455
+ port ||= (secure ? 465 : 25)
456
+ account ||= mail_from
457
+ login ||= :plain
458
+
459
+ #mail_to = nil if mail_to.empty?
460
+
461
+ raise ArgumentError, "missing email field -- server" unless server
462
+ raise ArgumentError, "missing email field -- account" unless account
463
+ raise ArgumentError, "missing email field -- subject" unless subject
464
+ raise ArgumentError, "missing email field -- to" unless mail_to
465
+ raise ArgumentError, "missing email field -- from" unless mail_from
466
+
467
+ passwd ||= password(account)
468
+
469
+ mail_to = [mail_to].flatten.compact
470
+
471
+ msg = ""
472
+ msg << "From: #{mail_from}\n"
473
+ msg << "To: #{mail_to.join(';')}\n"
474
+ msg << "Subject: #{subject}\n"
475
+ msg << ""
476
+ msg << message
477
+
478
+ begin
479
+ Net::SMTP.enable_tls if Net::SMTP.respond_to?(:enable_tls) and secure
480
+ Net::SMTP.start(server, port, domain, account, passwd, login) do |s|
481
+ s.send_message( msg, mail_from, mail_to )
482
+ end
483
+ puts "Email sent successfully to #{mail_to.join(';')}."
484
+ return true
485
+ rescue => e
486
+ if trace?
487
+ raise e
488
+ else
489
+ abort "Email delivery failed."
490
+ end
491
+ end
492
+ end
493
+
494
+ end
495
+
49
496
  end
50
497
 
51
- # run main task
52
- END { run_main }