senv 0.4.2

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/a.rb ADDED
@@ -0,0 +1,92 @@
1
+ task :dist => [:dist_rb] do
2
+ Dir.chdir(This.dir)
3
+
4
+ version = This.version
5
+ name = This.name.downcase
6
+
7
+ platforms = %w[
8
+ linux-x86 linux-x86_64 osx
9
+ ]
10
+
11
+ Dir.glob("./dist/#{ name }-*").each{|entry| FileUtils.rm_rf(entry)}
12
+
13
+ spawn = proc{|cmd| system(cmd) or abort("#{ cmd } #=> #{ $? }")}
14
+
15
+ entrypoint = File.expand_path("./dist/#{ name }.sh")
16
+
17
+ platforms.each do |platform|
18
+ distdir = "./dist/#{ name }-#{ version }-#{ platform }"
19
+ FileUtils.mkdir_p(distdir)
20
+
21
+ libdir = File.join(distdir, 'lib')
22
+ FileUtils.mkdir_p(libdir)
23
+
24
+ appdir = File.join(libdir, 'app')
25
+ FileUtils.mkdir_p(appdir)
26
+
27
+ rubydir = File.join(libdir, 'ruby')
28
+ FileUtils.mkdir_p(rubydir)
29
+
30
+ FileUtils.cp('./dist/senv.rb', appdir)
31
+
32
+ Dir.chdir(rubydir) do
33
+ basename = "traveling-ruby-20141215-2.1.5-#{ platform }.tar.gz"
34
+ cmd = "curl -s -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/#{ basename }"
35
+ spawn[cmd]
36
+ spawn["tar -xzf #{ basename }"]
37
+ FileUtils.rm(basename)
38
+ end
39
+
40
+ Dir.chdir(distdir) do
41
+ FileUtils.cp(entrypoint, name)
42
+ end
43
+ end
44
+
45
+ system('tree -L 4 dist 2>/dev/null') if STDOUT.tty?
46
+
47
+ Dir.chdir './dist' do
48
+ platforms.each do |platform|
49
+ dist = "#{ name }-#{ version }-#{ platform }"
50
+ spawn["tar cvfz #{ dist }.tgz ./#{ dist } >/dev/null 2>&1"]
51
+ FileUtils.rm_rf(dist)
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+
58
+
59
+ HELP = <<-____.tap{|s| s.gsub!(s[%r/^\s*/], '')}
60
+ VERSION
61
+ <%= Senv.version %>
62
+
63
+ TL;DR;
64
+
65
+ $ tree ./config/senvs/
66
+
67
+ config/senvs/
68
+ development.enc.rb
69
+ development.rb
70
+ development.yml
71
+ development.json
72
+ production.enc.rb
73
+ production.rb
74
+ production.yml
75
+ staging.json
76
+ staging.enc.rb
77
+ staging.rb
78
+ staging.yml
79
+ staging.json
80
+
81
+
82
+ ~> export SENV=production
83
+
84
+ ~> ./bin/senv run-something-in-production senv
85
+
86
+ ~> ./bin/senv @staging # show it
87
+
88
+ ~> ./bin/senv @staging run-something-in-staging-senv
89
+
90
+ ____
91
+
92
+ puts HELP
@@ -0,0 +1,259 @@
1
+ #! /usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ #
5
+ HELP = <<-____
6
+
7
+ NAME
8
+ senv - secure 12-factor environment variables
9
+
10
+ VERSION
11
+ <%= Senv.version %>
12
+
13
+ TL;DR;
14
+
15
+ # WIP ...
16
+
17
+ ____
18
+
19
+ #
20
+ require "pathname"
21
+ script_d = Pathname.new(__FILE__).realpath.dirname.to_s
22
+
23
+ unless defined?(Senv)
24
+ require File.expand_path("../lib/senv", script_d)
25
+ end
26
+
27
+ unless defined?(Senv::Script)
28
+ require File.expand_path("../lib/senv/script", script_d)
29
+ end
30
+
31
+ #
32
+ Senv.script do
33
+ before do
34
+ $handle_senv_alias = proc do
35
+ if ARGV.first =~ /^@/
36
+ ENV['SENV'] = ARGV.shift.sub(/^@/, '').strip
37
+ end
38
+ end.call
39
+
40
+ $handle_old_fashioned_cries_for_help = proc do
41
+ if ARGV.delete('-h') || ARGV.delete('--help')
42
+ ARGV.unshift('help')
43
+ end
44
+ end.call
45
+ end
46
+
47
+ #
48
+ run do
49
+ exec!
50
+ end
51
+
52
+ #
53
+ run 'help' do
54
+ show_help!
55
+ end
56
+
57
+ #
58
+ run 'exec' do
59
+ exec!
60
+ end
61
+
62
+ #
63
+ run 'init' do
64
+ require "shellwords"
65
+
66
+ load_senv!
67
+
68
+ Senv.environment.to_hash.each do |key, val|
69
+ if val
70
+ STDOUT.puts "export #{ Shellwords.escape(key) }=#{ Shellwords.escape(val) }"
71
+ else
72
+ STDOUT.puts "unset #{ Shellwords.escape(key) }"
73
+ end
74
+ end
75
+ end
76
+
77
+ #
78
+ run '.setup' do
79
+ dir = @argv.shift || Dir.pwd
80
+ key = @argv.shift || @options['key'] || SecureRandom.uuid
81
+
82
+ key.strip!
83
+
84
+ Dir.chdir(dir) do
85
+ if test(?d, '.senv')
86
+ abort "#{ dir }/.senv directory exists"
87
+ end
88
+
89
+ FileUtils.mkdir_p('.senv')
90
+
91
+ IO.binwrite('.senv/_key', "#{ key }\n")
92
+
93
+ Senv.key = key
94
+
95
+ Senv.write(
96
+ '.senv/all.rb',
97
+ u.unindent(
98
+ <<-____
99
+ ENV['A'] = 'one'
100
+ ENV['B'] = 'two'
101
+ ENV['C'] = 'three'
102
+ ____
103
+ )
104
+ )
105
+
106
+ %w[ development production ].each do |env|
107
+ Senv.write(
108
+ ".senv/#{ env }.rb",
109
+ u.unindent(
110
+ <<-____
111
+ Senv.load(:all)
112
+
113
+ ENV['B'] = 'two (via #{ env }.rb)'
114
+ ____
115
+ )
116
+ )
117
+
118
+ Senv.write(
119
+ ".senv/#{ env }.enc.rb",
120
+ u.unindent(
121
+ <<-____
122
+ Senv.load(:all)
123
+
124
+ ENV['C'] = 'three (via #{ env }.enc.rb)'
125
+ ____
126
+ )
127
+ )
128
+ end
129
+
130
+ puts "[SENV] setup #{ File.expand_path(dir) }"
131
+
132
+ Dir.glob('.senv/**/**').sort.each do |entry|
133
+ next unless test(?f, entry)
134
+ puts "- #{ entry }"
135
+ end
136
+ end
137
+ end
138
+
139
+ #
140
+ run '.inspect' do
141
+ load_senv!
142
+
143
+ if @argv.empty?
144
+ puts Senv.environment.inspect
145
+ else
146
+ env = {}
147
+ @argv.each do |name|
148
+ env[name] = Senv.environment[name]
149
+ end
150
+ puts env.inspect
151
+ end
152
+ end
153
+
154
+ #
155
+ run '.edit' do
156
+ input = @argv[0]
157
+ output = input
158
+ options = @opts
159
+
160
+ data =
161
+ if test(?s, input)
162
+ Senv.read(input, options)
163
+ else
164
+ ''
165
+ end
166
+
167
+ ext = File.basename(input).split('.').last
168
+ editor = @options['editor'] || ENV['EDITOR'] || 'vim'
169
+
170
+ u.tmpfile(:ext => ext) do |tmp|
171
+ tmp.write(data)
172
+ tmp.close
173
+
174
+ if(system "#{ editor } #{ tmp.path }")
175
+ data = IO.binread(tmp.path)
176
+ Senv.write(output, data, options)
177
+ end
178
+ end
179
+ end
180
+
181
+ #
182
+ run '.read' do
183
+ input = @argv[0] || '-'
184
+ output = @argv[1] || '-'
185
+ options = @options
186
+
187
+ data =
188
+ if input == '-'
189
+ Senv.read('/dev/stdin', options)
190
+ else
191
+ Senv.read(input, options)
192
+ end
193
+
194
+ if output == '-'
195
+ Senv.write('/dev/stdout', data, options)
196
+ else
197
+ Senv.write(output, data, options)
198
+ end
199
+ end
200
+
201
+ run '.write' do
202
+ output = @argv[0] || '-'
203
+ input = @argv[1] || '-'
204
+ options = @options
205
+
206
+ data =
207
+ if input == '-'
208
+ Senv.read('/dev/stdin')
209
+ else
210
+ Senv.read(input)
211
+ end
212
+
213
+ if output == '-'
214
+ Senv.write('/dev/stdout', data, options)
215
+ else
216
+ Senv.write(output, data, options)
217
+ end
218
+ end
219
+
220
+ run '.get' do
221
+ load_senv!
222
+
223
+ @argv.each do |name|
224
+ puts ENV[name]
225
+ end
226
+ end
227
+
228
+ #
229
+ def show_help!
230
+ help = ERB.new(u.unindent(HELP)).result(::TOPLEVEL_BINDING)
231
+ STDOUT.puts(help)
232
+ end
233
+
234
+ def load_senv!
235
+ if @options['debug']
236
+ ENV['SENV_DEBUG'] = 'true'
237
+ end
238
+
239
+ if @options['key']
240
+ ENV['SENV_KEY'] = @options['key']
241
+ end
242
+
243
+ begin
244
+ Senv.load(:force => @options['force'])
245
+ rescue Senv::Error => e
246
+ abort(e.message)
247
+ end
248
+ end
249
+
250
+ def exec!
251
+ load_senv!
252
+
253
+ if @argv.empty?
254
+ STDOUT.puts Senv.environment.to_hash.to_yaml
255
+ else
256
+ exec(*@argv)
257
+ end
258
+ end
259
+ end
Binary file
@@ -0,0 +1,1468 @@
1
+ #! /usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ #
5
+ HELP = <<-____
6
+
7
+ NAME
8
+ senv - secure 12-factor environment variables
9
+
10
+ VERSION
11
+ <%= Senv.version %>
12
+
13
+ TL;DR;
14
+
15
+ # WIP ...
16
+
17
+ ____
18
+
19
+ #
20
+ require "pathname"
21
+ script_d = Pathname.new(__FILE__).realpath.dirname.to_s
22
+
23
+ unless defined?(Senv)
24
+ require File.expand_path("../lib/senv", script_d)
25
+ end
26
+
27
+ unless defined?(Senv::Script)
28
+ require File.expand_path("../lib/senv/script", script_d)
29
+ end
30
+
31
+ #
32
+ Senv.script do
33
+ before do
34
+ $handle_senv_alias = proc do
35
+ if ARGV.first =~ /^@/
36
+ ENV['SENV'] = ARGV.shift.sub(/^@/, '').strip
37
+ end
38
+ end.call
39
+
40
+ $handle_old_fashioned_cries_for_help = proc do
41
+ if ARGV.delete('-h') || ARGV.delete('--help')
42
+ ARGV.unshift('help')
43
+ end
44
+ end.call
45
+ end
46
+
47
+ #
48
+ run do
49
+ exec!
50
+ end
51
+
52
+ #
53
+ run 'help' do
54
+ show_help!
55
+ end
56
+
57
+ #
58
+ run 'exec' do
59
+ exec!
60
+ end
61
+
62
+ #
63
+ run 'init' do
64
+ require "shellwords"
65
+
66
+ load_senv!
67
+
68
+ Senv.environment.to_hash.each do |key, val|
69
+ if val
70
+ STDOUT.puts "export #{ Shellwords.escape(key) }=#{ Shellwords.escape(val) }"
71
+ else
72
+ STDOUT.puts "unset #{ Shellwords.escape(key) }"
73
+ end
74
+ end
75
+ end
76
+
77
+ #
78
+ run '.setup' do
79
+ dir = @argv.shift || Dir.pwd
80
+ key = @argv.shift || @options['key'] || SecureRandom.uuid
81
+
82
+ key.strip!
83
+
84
+ Dir.chdir(dir) do
85
+ if test(?d, '.senv')
86
+ abort "#{ dir }/.senv directory exists"
87
+ end
88
+
89
+ FileUtils.mkdir_p('.senv')
90
+
91
+ IO.binwrite('.senv/_key', "#{ key }\n")
92
+
93
+ Senv.key = key
94
+
95
+ Senv.write(
96
+ '.senv/all.rb',
97
+ u.unindent(
98
+ <<-____
99
+ ENV['A'] = 'one'
100
+ ENV['B'] = 'two'
101
+ ENV['C'] = 'three'
102
+ ____
103
+ )
104
+ )
105
+
106
+ %w[ development production ].each do |env|
107
+ Senv.write(
108
+ ".senv/#{ env }.rb",
109
+ u.unindent(
110
+ <<-____
111
+ Senv.load(:all)
112
+
113
+ ENV['B'] = 'two (via #{ env }.rb)'
114
+ ____
115
+ )
116
+ )
117
+
118
+ Senv.write(
119
+ ".senv/#{ env }.enc.rb",
120
+ u.unindent(
121
+ <<-____
122
+ Senv.load(:all)
123
+
124
+ ENV['C'] = 'three (via #{ env }.enc.rb)'
125
+ ____
126
+ )
127
+ )
128
+ end
129
+
130
+ puts "[SENV] setup #{ File.expand_path(dir) }"
131
+
132
+ Dir.glob('.senv/**/**').sort.each do |entry|
133
+ next unless test(?f, entry)
134
+ puts "- #{ entry }"
135
+ end
136
+ end
137
+ end
138
+
139
+ #
140
+ run '.inspect' do
141
+ load_senv!
142
+
143
+ if @argv.empty?
144
+ puts Senv.environment.inspect
145
+ else
146
+ env = {}
147
+ @argv.each do |name|
148
+ env[name] = Senv.environment[name]
149
+ end
150
+ puts env.inspect
151
+ end
152
+ end
153
+
154
+ #
155
+ run '.edit' do
156
+ input = @argv[0]
157
+ output = input
158
+ options = @opts
159
+
160
+ data =
161
+ if test(?s, input)
162
+ Senv.read(input, options)
163
+ else
164
+ ''
165
+ end
166
+
167
+ ext = File.basename(input).split('.').last
168
+ editor = @options['editor'] || ENV['EDITOR'] || 'vim'
169
+
170
+ u.tmpfile(:ext => ext) do |tmp|
171
+ tmp.write(data)
172
+ tmp.close
173
+
174
+ if(system "#{ editor } #{ tmp.path }")
175
+ data = IO.binread(tmp.path)
176
+ Senv.write(output, data, options)
177
+ end
178
+ end
179
+ end
180
+
181
+ #
182
+ run '.read' do
183
+ input = @argv[0] || '-'
184
+ output = @argv[1] || '-'
185
+ options = @options
186
+
187
+ data =
188
+ if input == '-'
189
+ Senv.read('/dev/stdin', options)
190
+ else
191
+ Senv.read(input, options)
192
+ end
193
+
194
+ if output == '-'
195
+ Senv.write('/dev/stdout', data, options)
196
+ else
197
+ Senv.write(output, data, options)
198
+ end
199
+ end
200
+
201
+ run '.write' do
202
+ output = @argv[0] || '-'
203
+ input = @argv[1] || '-'
204
+ options = @options
205
+
206
+ data =
207
+ if input == '-'
208
+ Senv.read('/dev/stdin')
209
+ else
210
+ Senv.read(input)
211
+ end
212
+
213
+ if output == '-'
214
+ Senv.write('/dev/stdout', data, options)
215
+ else
216
+ Senv.write(output, data, options)
217
+ end
218
+ end
219
+
220
+ run '.get' do
221
+ load_senv!
222
+
223
+ @argv.each do |name|
224
+ puts ENV[name]
225
+ end
226
+ end
227
+
228
+ #
229
+ def show_help!
230
+ help = ERB.new(u.unindent(HELP)).result(::TOPLEVEL_BINDING)
231
+ STDOUT.puts(help)
232
+ end
233
+
234
+ def load_senv!
235
+ if @options['debug']
236
+ ENV['SENV_DEBUG'] = 'true'
237
+ end
238
+
239
+ if @options['key']
240
+ ENV['SENV_KEY'] = @options['key']
241
+ end
242
+
243
+ begin
244
+ Senv.load(:force => @options['force'])
245
+ rescue Senv::Error => e
246
+ abort(e.message)
247
+ end
248
+ end
249
+
250
+ def exec!
251
+ load_senv!
252
+
253
+ if @argv.empty?
254
+ STDOUT.puts Senv.environment.to_hash.to_yaml
255
+ else
256
+ exec(*@argv)
257
+ end
258
+ end
259
+ end
260
+
261
+
262
+ BEGIN {
263
+
264
+ ### <lib src='lib/senv.rb'>
265
+
266
+ #
267
+ require 'erb'
268
+ require 'yaml'
269
+ require 'json'
270
+ require 'rbconfig'
271
+ require 'pp'
272
+ require 'time'
273
+ require 'fileutils'
274
+ require 'pathname'
275
+ require 'thread'
276
+ require 'openssl'
277
+ require 'tmpdir'
278
+ require 'securerandom'
279
+
280
+ #
281
+ module Senv
282
+ #
283
+ VERSION = '0.4.2'.freeze
284
+
285
+ def Senv.version
286
+ VERSION
287
+ end
288
+
289
+ #
290
+ LICENSE = 'MIT'.freeze
291
+
292
+ def Senv.license
293
+ LICENSE
294
+ end
295
+
296
+ #
297
+ SUMMARY = ''
298
+
299
+ #
300
+ DEFAULT = 'development'.freeze
301
+
302
+ def Senv.default
303
+ DEFAULT
304
+ end
305
+
306
+ #
307
+ class Error < StandardError
308
+ end
309
+
310
+ def Senv.error!(*args, &block)
311
+ raise Error.new(*args, &block)
312
+ end
313
+
314
+ #
315
+ def Senv.env
316
+ ENV['SENV']
317
+ end
318
+
319
+ def Senv.env=(env)
320
+ if env
321
+ ENV['SENV'] = env.to_s.strip
322
+ else
323
+ ENV.delete('SENV')
324
+ end
325
+ end
326
+
327
+ #
328
+ def Senv.load(*args)
329
+ Senv.thread_safe do
330
+ #
331
+ env, options = Senv.parse_load_args(*args)
332
+
333
+ #
334
+ force = !!(options['force'] || options[:force])
335
+
336
+ #
337
+ a_parent_process_has_already_loaded_the_senv = (
338
+ ENV['SENV'] == env &&
339
+ ENV['SENV_LOADED'] &&
340
+ ENV['SENV_ENVIRONMENT']
341
+ )
342
+
343
+ if(a_parent_process_has_already_loaded_the_senv && !force)
344
+ Senv.env = env
345
+ Senv.loaded = JSON.parse(ENV['SENV_LOADED'])
346
+ Senv.environment = JSON.parse(ENV['SENV_ENVIRONMENT'])
347
+ return env
348
+ end
349
+
350
+ #
351
+ unless Senv.loading
352
+ loading = Senv.loading
353
+
354
+ begin
355
+ Senv.loading = env
356
+
357
+ Senv.loaded.clear
358
+ Senv.environment.clear
359
+
360
+ Senv.load_config_paths_for(env)
361
+
362
+ Senv.env = env
363
+
364
+ ENV['SENV'] = Senv.env
365
+ ENV['SENV_LOADED'] = JSON.generate(Senv.loaded)
366
+ ENV['SENV_ENVIRONMENT'] = JSON.generate(Senv.environment)
367
+
368
+ return env
369
+ ensure
370
+ Senv.loading = loading
371
+ end
372
+ else
373
+ a_config_file_imports_another_senv = (
374
+ Senv.loading != env
375
+ )
376
+
377
+ if a_config_file_imports_another_senv
378
+ Senv.load_config_paths_for(env)
379
+ return env
380
+ end
381
+
382
+ a_config_file_imports_itself_recursively = (
383
+ Senv.loading == env
384
+ )
385
+
386
+ if a_config_file_imports_itself_recursively
387
+ :cowardly_refuse_to_infinitely_recurse
388
+ return nil
389
+ end
390
+ end
391
+ end
392
+ end
393
+
394
+ def Senv.load!(*args)
395
+ env, options = Senv.parse_load_args(*args)
396
+ options['force'] = options[:force] = true
397
+ Senv.load(env, options)
398
+ end
399
+
400
+ def Senv.parse_load_args(*args)
401
+ env = Senv.env || Senv.default
402
+ options = Hash.new
403
+
404
+ case args.first
405
+ when String, Symbol
406
+ env = args.shift.to_s
407
+ end
408
+
409
+ case args.first
410
+ when Hash
411
+ options = args.shift
412
+ end
413
+
414
+ [env, options]
415
+ end
416
+
417
+ def Senv.thread_safe(&block)
418
+ THREAD_SAFE.synchronize(&block)
419
+ end
420
+ THREAD_SAFE = ::Monitor.new
421
+
422
+ def Senv.load_config_paths_for(env)
423
+ paths = Senv.config_paths_for(env)
424
+ Senv.load_config_paths(*paths)
425
+ end
426
+
427
+ def Senv.config_paths_for(env)
428
+ glob = "**/#{ env }.{rb,enc.rb}"
429
+
430
+ Senv.directory.glob(glob).sort_by do |path|
431
+ ext = path.basename.extname.split('.')
432
+ [path.basename.to_s.size, ext.size]
433
+ end
434
+ end
435
+
436
+ def Senv.load_config_paths(*paths)
437
+ libs = []
438
+ configs = []
439
+
440
+ paths.each do |path|
441
+ exts = path.extname.split('.')[1..-1]
442
+
443
+ case
444
+ when exts.include?('rb')
445
+ libs << path
446
+ else
447
+ configs << path
448
+ end
449
+ end
450
+
451
+ {
452
+ libs => :load_lib,
453
+ configs => :load_config,
454
+ }.each do |list, loader|
455
+ list.each do |path|
456
+ path = path.to_s
457
+
458
+ Senv.debug({'loading' => path})
459
+
460
+ if Senv.loaded.has_key?(path)
461
+ Senv.debug({'skipping' => path})
462
+ next
463
+ end
464
+
465
+ Senv.loaded[path] = nil
466
+
467
+ captured =
468
+ Senv.capturing_environment_changes do
469
+ Senv.send(loader, path)
470
+ end
471
+
472
+ changes = captured.changes
473
+
474
+ Senv.debug({path => changes})
475
+
476
+ Senv.loaded[path] = changes
477
+
478
+ captured.apply(Senv.environment)
479
+ end
480
+ end
481
+ end
482
+
483
+ #
484
+ def Senv.loading
485
+ @loading
486
+ end
487
+
488
+ def Senv.loading=(env)
489
+ @loading = env.to_s.strip
490
+ end
491
+
492
+ def Senv.loaded
493
+ @loaded ||= {}
494
+ end
495
+
496
+ def Senv.loaded=(hash)
497
+ @loaded = hash
498
+ end
499
+
500
+ def Senv.environment
501
+ @environment ||= {}
502
+ end
503
+
504
+ def Senv.environment=(hash)
505
+ @environment = hash
506
+ end
507
+
508
+ #
509
+ def Senv.realpath(path)
510
+ Pathname.new(path.to_s).realpath
511
+ end
512
+
513
+ def Senv.expand_path(path)
514
+ (realpath(path) rescue File.expand_path(path)).to_s
515
+ end
516
+
517
+ #
518
+ def Senv.root
519
+ determine_root! unless @root
520
+ @root
521
+ end
522
+
523
+ def Senv.root=(root)
524
+ @root = realpath(root)
525
+ end
526
+
527
+ def Senv.determine_root!
528
+ if ENV['SENV_ROOT']
529
+ Senv.root = ENV['SENV_ROOT']
530
+ return @root
531
+ else
532
+ Senv.search_path.each do |dirname|
533
+ if test(?d, dirname)
534
+ Senv.root = dirname
535
+ return @root
536
+ end
537
+ end
538
+ end
539
+
540
+ msg = "[SENV] no `.senv` directory found via `#{ Senv.search_path.join(' | ') }`"
541
+ Senv.error!(msg)
542
+ end
543
+
544
+ def Senv.directory
545
+ Senv.root.join('.senv')
546
+ end
547
+
548
+ def Senv.search_path
549
+ search_path = []
550
+
551
+ if ENV['SENV_PATH']
552
+ ENV['SENV_PATH'].split(':').each do |path|
553
+ search_path << Senv.expand_path(path).to_s
554
+ end
555
+ else
556
+ Pathname.pwd.realpath.ascend do |path|
557
+ search_path << path.to_s
558
+ end
559
+ end
560
+
561
+ search_path
562
+ end
563
+
564
+ def Senv.key_path
565
+ Senv.directory.join('_key')
566
+ end
567
+
568
+ def Senv.key
569
+ if ENV['SENV_KEY']
570
+ ENV['SENV_KEY']
571
+ else
572
+ if Senv.key_path.exist?
573
+ Senv.key_path.binread.strip
574
+ else
575
+ msg = "Senv.key not found in : #{ Senv.key_path }"
576
+ Senv.error!(msg)
577
+ end
578
+ end
579
+ end
580
+
581
+ def Senv.key=(key)
582
+ ENV['SENV_KEY'] = key.to_s.strip
583
+ end
584
+
585
+ def Senv.key_source
586
+ if ENV['SENV_KEY']
587
+ "ENV['SENV_KEY']"
588
+ else
589
+ Senv.key_path rescue '(no key source)'
590
+ end
591
+ end
592
+
593
+ #
594
+ ENCRYPTED_PATH_RE = Regexp.union(/\.enc(rypted)$/, /\.enc(rypted)?\./)
595
+
596
+ def Senv.is_encrypted?(path)
597
+ path.to_s =~ ENCRYPTED_PATH_RE
598
+ end
599
+
600
+ #
601
+ def Senv.binread(path, options = {})
602
+ data = IO.binread(path)
603
+
604
+ encrypted =
605
+ if options.has_key?('encrypted')
606
+ options['encrypted']
607
+ else
608
+ Senv.is_encrypted?(path)
609
+ end
610
+
611
+ if encrypted
612
+ data =
613
+ begin
614
+ Blowfish.decrypt(Senv.key, data)
615
+ rescue
616
+ abort "could not decrypt `#{ path }` with key `#{ Senv.key }` from `#{ Senv.key_source }`"
617
+ end
618
+ end
619
+
620
+ data
621
+ end
622
+
623
+ def Senv.read(path, options = {})
624
+ Senv.binread(path)
625
+ end
626
+
627
+ def Senv.binwrite(path, data, options = {})
628
+ encrypted =
629
+ if options.has_key?('encrypted')
630
+ options['encrypted']
631
+ else
632
+ Senv.is_encrypted?(path)
633
+ end
634
+
635
+ if encrypted
636
+ data =
637
+ begin
638
+ Blowfish.encrypt(Senv.key, data)
639
+ rescue
640
+ abort "could not encrypt `#{ data.to_s.split("\n").first }...` with key `#{ Senv.key }` from `#{ Senv.key_source }`"
641
+ end
642
+ end
643
+
644
+ IO.binwrite(path, data)
645
+ end
646
+
647
+ def Senv.write(path, data, options = {})
648
+ Senv.binwrite(path, data, options)
649
+ end
650
+
651
+ def Senv.load_lib(path)
652
+ #
653
+ code = Senv.binread(path.to_s)
654
+ binding = ::TOPLEVEL_BINDING
655
+ filename = path.to_s
656
+
657
+ #
658
+ Kernel.eval(code, binding, filename)
659
+ end
660
+
661
+ def Senv.load_config(path)
662
+ #
663
+ erb = Senv.binread(path)
664
+ expanded = ERB.new(erb).result(::TOPLEVEL_BINDING)
665
+ buf = expanded
666
+
667
+ #
668
+ encoded = buf
669
+
670
+ config =
671
+ case
672
+ when path =~ /yml|yaml/
673
+ YAML.load(encoded)
674
+ when path =~ /json/
675
+ JSON.parse(encoded)
676
+ else
677
+ abort "unknown config format in #{ path }"
678
+ end
679
+
680
+ #
681
+ unless config && config.is_a?(Hash)
682
+ abort "[SENV] failed to load #{ path }"
683
+ end
684
+
685
+ #
686
+ config.each do |key, val|
687
+ ENV[key.to_s] = val.to_s
688
+ end
689
+
690
+ #
691
+ config
692
+ end
693
+
694
+ #
695
+ def Senv.debug(*args, &block)
696
+ if args.empty? && block.nil?
697
+ return Senv.debug?
698
+ end
699
+
700
+ return nil unless Senv.debug?
701
+
702
+ lines = []
703
+
704
+ args.each do |arg|
705
+ case
706
+ when arg.is_a?(String)
707
+ lines << arg.strip
708
+ else
709
+ lines << arg.inspect.strip
710
+ end
711
+ end
712
+
713
+ return nil if(lines.empty? && block.nil?)
714
+
715
+ if lines
716
+ lines.each do |line|
717
+ STDERR.puts "# [SENV=#{ Senv.env }] : #{ line }"
718
+ end
719
+ end
720
+
721
+ if block
722
+ return block.call
723
+ else
724
+ true
725
+ end
726
+ end
727
+
728
+ def Senv.debug?
729
+ !!ENV['SENV_DEBUG']
730
+ end
731
+
732
+ def Senv.debug=(arg)
733
+ if arg
734
+ ENV['SENV_DEBUG'] = 'true'
735
+ else
736
+ ENV.delete('SENV_DEBUG')
737
+ end
738
+ end
739
+
740
+ #
741
+ def Senv.senvs
742
+ @senvs ||= Hash.new
743
+ end
744
+
745
+ def Senv.for_senv!(senv)
746
+ senv = senv.to_s
747
+
748
+ IO.popen('-', 'w+') do |io|
749
+ child = io.nil?
750
+
751
+ if child
752
+ Senv.load(senv)
753
+ puts Senv.environment.to_yaml
754
+ exit
755
+ else
756
+ YAML.load(io.read)
757
+ end
758
+ end
759
+ end
760
+
761
+ def Senv.for_senv(senv)
762
+ senv = senv.to_s
763
+
764
+ if Senv.senvs.has_key?(senv)
765
+ return Senv.senvs[senv]
766
+ end
767
+
768
+ Senv.senvs[senv] = Senv.for_senv!(senv)
769
+ end
770
+
771
+ def Senv.get(senv, var)
772
+ Senv.for_senv(senv)[var.to_s]
773
+ end
774
+
775
+ def Senv.get!(senv, var)
776
+ Senv.for_senv!(senv)[var.to_s]
777
+ end
778
+
779
+ #
780
+ module Blowfish
781
+ def cipher(senv, key, data)
782
+ cipher = OpenSSL::Cipher.new('bf-cbc').send(senv)
783
+ cipher.key = Digest::SHA256.digest(key.to_s).slice(0,16)
784
+ cipher.update(data) << cipher.final
785
+ end
786
+
787
+ def encrypt(key, data)
788
+ cipher(:encrypt, key, data)
789
+ end
790
+
791
+ def decrypt(key, text)
792
+ cipher(:decrypt, key, text)
793
+ end
794
+
795
+ def cycle(key, data)
796
+ decrypt(key, encrypt(key, data))
797
+ end
798
+
799
+ def recrypt(old_key, new_key, data)
800
+ encrypt(new_key, decrypt(old_key, data))
801
+ end
802
+
803
+ extend(self)
804
+ end
805
+
806
+ #
807
+ def Senv.capturing_environment_changes(&block)
808
+ EnvChangeTracker.track(&block)
809
+ end
810
+
811
+ class EnvChangeTracker < ::BasicObject
812
+ def initialize(env)
813
+ @env = env
814
+
815
+ @changes = {
816
+ :deleted => [],
817
+ :updated => [],
818
+ :created => [],
819
+ }
820
+
821
+ @change_for = proc do |key, val|
822
+ if @env.has_key?(key)
823
+ case
824
+ when val.nil?
825
+ {:type => :deleted, :info => [key, val]}
826
+ when val.to_s != @env[key].to_s
827
+ {:type => :updated, :info => [key, val]}
828
+ else
829
+ nil
830
+ end
831
+ else
832
+ {:type => :created, :info => [key, val]}
833
+ end
834
+ end
835
+
836
+ @track_change = proc do |key, val|
837
+ change = @change_for[key, val]
838
+
839
+ if change
840
+ @changes[change[:type]].push(change[:info])
841
+ end
842
+ end
843
+ end
844
+
845
+ def changes
846
+ @changes
847
+ end
848
+
849
+ def method_missing(method, *args, &block)
850
+ @env.send(method, *args, &block)
851
+ end
852
+
853
+ def []=(key, val)
854
+ @track_change[key, val]
855
+ @env[key] = val
856
+ end
857
+
858
+ def replace(hash)
859
+ hash.each do |key, val|
860
+ @track_change[key, val]
861
+ end
862
+ @env.replace(hash)
863
+ end
864
+
865
+ def store(key, val)
866
+ @track_change[key, val]
867
+ @env.store(key, val)
868
+ end
869
+
870
+ def delete(key)
871
+ @track_change[key, nil]
872
+ @env.delete(key)
873
+ end
874
+
875
+ def apply(env)
876
+ @changes[:created].each do |k, v|
877
+ env[k] = v
878
+ end
879
+ @changes[:updated].each do |k, v|
880
+ env[k] = v
881
+ end
882
+ @changes[:deleted].each do |k, v|
883
+ env.delete(k)
884
+ end
885
+ @changes
886
+ end
887
+
888
+ THREAD_SAFE = ::Monitor.new
889
+
890
+ def EnvChangeTracker.track(&block)
891
+ THREAD_SAFE.synchronize do
892
+ env = EnvChangeTracker.new(::ENV)
893
+
894
+ ::Object.send(:remove_const, :ENV)
895
+ ::Object.send(:const_set, :ENV, env)
896
+
897
+ begin
898
+ block.call
899
+ env
900
+ ensure
901
+ ::Object.send(:remove_const, :ENV)
902
+ ::Object.send(:const_set, :ENV, env)
903
+ end
904
+ end
905
+ end
906
+ end
907
+
908
+ #
909
+ class ::Pathname
910
+ unless ::Pathname.pwd.respond_to?(:glob)
911
+ def glob(glob, &block)
912
+ paths = []
913
+
914
+ Dir.glob("#{ self }/#{ glob }") do |entry|
915
+ path = Pathname.new(entry)
916
+
917
+ if block
918
+ block.call(path)
919
+ else
920
+ paths.push(path)
921
+ end
922
+ end
923
+
924
+ block ? nil : paths
925
+ end
926
+ end
927
+ end
928
+ end
929
+
930
+
931
+ ### </lib src='lib/senv.rb'>
932
+
933
+ ### <lib src='lib/senv/script.rb'>
934
+
935
+ #! /usr/bin/env ruby
936
+ #
937
+ require 'json'
938
+ require 'yaml'
939
+ require 'base64'
940
+ require 'securerandom'
941
+ require 'fileutils'
942
+ require 'pathname'
943
+ require 'set'
944
+ require 'openssl'
945
+ require 'uri'
946
+ require 'cgi'
947
+ require 'shellwords'
948
+ require 'tmpdir'
949
+ require 'tempfile'
950
+ require 'pp'
951
+ require 'open3'
952
+
953
+ #
954
+ module Senv
955
+ class Script
956
+ attr_accessor :source
957
+ attr_accessor :root
958
+ attr_accessor :env
959
+ attr_accessor :argv
960
+ attr_accessor :stdout
961
+ attr_accessor :stdin
962
+ attr_accessor :stderr
963
+ attr_accessor :help
964
+
965
+ def run!(env = ENV, argv = ARGV)
966
+ before!
967
+ init!(env, argv)
968
+ parse_command_line!
969
+ set_mode!
970
+ run_mode!
971
+ after!
972
+ end
973
+
974
+ def init!(env, argv)
975
+ @klass = self.class
976
+ @env = env.to_hash.dup
977
+ @argv = argv.map{|arg| arg.dup}
978
+ @stdout = $stdout.dup
979
+ @stdin = $stdin.dup
980
+ @stderr = $stderr.dup
981
+ @help = @klass.help || Util.unindent(DEFAULT_HELP)
982
+ end
983
+
984
+ def self.before(&block)
985
+ @before ||= []
986
+ @before << block if block
987
+ @before
988
+ end
989
+
990
+ def before!
991
+ self.class.before.each{|block| block.call}
992
+ end
993
+
994
+ def self.after(&block)
995
+ @after ||= []
996
+ @after << block if block
997
+ @after
998
+ end
999
+
1000
+ def after!
1001
+ self.class.after.each{|block| block.call}
1002
+ end
1003
+
1004
+ DEFAULT_HELP = <<-__
1005
+ NAME
1006
+ #TODO
1007
+
1008
+ SYNOPSIS
1009
+ #TODO
1010
+
1011
+ DESCRIPTION
1012
+ #TODO
1013
+
1014
+ EXAMPLES
1015
+ #TODO
1016
+ __
1017
+
1018
+ def parse_command_line!
1019
+ @options = Hash.new
1020
+ @opts = Hash.new
1021
+
1022
+ argv = []
1023
+ head = []
1024
+ tail = []
1025
+
1026
+ %w[ :: -- ].each do |stop|
1027
+ if((i = @argv.index(stop)))
1028
+ head = @argv.slice(0 ... i)
1029
+ tail = @argv.slice((i + 1) ... @argv.size)
1030
+ @argv = head
1031
+ break
1032
+ end
1033
+ end
1034
+
1035
+ @argv.each do |arg|
1036
+ case
1037
+ when arg =~ %r`^\s*:([^:\s]+)[=](.+)`
1038
+ key = $1
1039
+ val = $2
1040
+ @options[key] = val
1041
+ when arg =~ %r`^\s*(:+)(.+)`
1042
+ switch = $1
1043
+ key = $2
1044
+ val = switch.size.odd?
1045
+ @options[key] = val
1046
+ else
1047
+ argv.push(arg)
1048
+ end
1049
+ end
1050
+
1051
+ argv += tail
1052
+
1053
+ @argv.replace(argv)
1054
+
1055
+ @options.each do |key, val|
1056
+ @opts[key.to_s.to_sym] = val
1057
+ end
1058
+
1059
+ [@options, @opts]
1060
+ end
1061
+
1062
+ def set_mode!
1063
+ case
1064
+ when respond_to?("run_#{ @argv[0] }")
1065
+ @mode = @argv.shift
1066
+ else
1067
+ @mode = nil
1068
+ end
1069
+ end
1070
+
1071
+ def run_mode!
1072
+ if @mode
1073
+ return send("run_#{ @mode }")
1074
+ else
1075
+ if respond_to?(:run)
1076
+ return send(:run)
1077
+ end
1078
+
1079
+ if @argv.empty?
1080
+ run_help
1081
+ else
1082
+ abort("#{ $0 } help")
1083
+ end
1084
+ end
1085
+ end
1086
+
1087
+ def run_help
1088
+ STDOUT.puts(@help)
1089
+ end
1090
+
1091
+ def help!
1092
+ run_help
1093
+ abort
1094
+ end
1095
+
1096
+ #
1097
+ module Util
1098
+ def unindent(arg)
1099
+ string = arg.to_s.dup
1100
+ margin = nil
1101
+ string.each_line do |line|
1102
+ next if line =~ %r/^\s*$/
1103
+ margin = line[%r/^\s*/] and break
1104
+ end
1105
+ string.gsub!(%r/^#{ margin }/, "") if margin
1106
+ margin ? string : nil
1107
+ end
1108
+
1109
+ def esc(*args)
1110
+ args.flatten.compact.map{|arg| Shellwords.escape(arg)}.join(' ')
1111
+ end
1112
+
1113
+ def uuid
1114
+ SecureRandom.uuid
1115
+ end
1116
+
1117
+ def tmpname(*args)
1118
+ opts = extract_options!(*args)
1119
+
1120
+ base = opts.fetch(:base){ uuid }.to_s.strip
1121
+ ext = opts.fetch(:ext){ 'tmp' }.to_s.strip.sub(/^[.]+/, '')
1122
+ basename = opts.fetch(:basename){ "#{ base }.#{ ext }" }
1123
+
1124
+ File.join(Dir.tmpdir, basename)
1125
+ end
1126
+
1127
+ def tmpfile(*args, &block)
1128
+ opts = extract_options!(args)
1129
+
1130
+ path = tmpname(opts)
1131
+
1132
+
1133
+ tmp = open(path, 'w+')
1134
+ tmp.binmode
1135
+ tmp.sync = true
1136
+
1137
+ unless args.empty?
1138
+ src = args.join
1139
+ tmp.write(src)
1140
+ tmp.flush
1141
+ tmp.rewind
1142
+ end
1143
+
1144
+ if block
1145
+ begin
1146
+ block.call(tmp)
1147
+ ensure
1148
+ FileUtils.rm_rf(path)
1149
+ end
1150
+ else
1151
+ at_exit{ Kernel.system("rm -rf #{ esc(path) }") }
1152
+ return tmp
1153
+ end
1154
+ end
1155
+
1156
+ def extract_options!(args)
1157
+ unless args.is_a?(Array)
1158
+ args = [args]
1159
+ end
1160
+
1161
+ opts = args.last.is_a?(Hash) ? args.pop : {}
1162
+
1163
+ symbolize_keys!(opts)
1164
+
1165
+ return opts
1166
+ end
1167
+
1168
+ def extract_options(args)
1169
+ opts = extract_options!(args)
1170
+
1171
+ args.push(opts)
1172
+
1173
+ opts
1174
+ end
1175
+
1176
+ def symbolize_keys!(hash)
1177
+ hash.keys.each do |key|
1178
+ if key.is_a?(String)
1179
+ val = hash.delete(key)
1180
+
1181
+ if val.is_a?(Hash)
1182
+ symbolize_keys!(val)
1183
+ end
1184
+
1185
+ hash[key.to_s.gsub('-', '_').to_sym] = val
1186
+ end
1187
+ end
1188
+
1189
+ return hash
1190
+ end
1191
+
1192
+ def symbolize_keys(hash)
1193
+ symbolize_keys!(copy(hash))
1194
+ end
1195
+
1196
+ def copy(object)
1197
+ Marshal.load(Marshal.dump(object))
1198
+ end
1199
+
1200
+ def debug!(arg)
1201
+ if arg.is_a?(String)
1202
+ warn "[DEBUG] #{ arg }"
1203
+ else
1204
+ warn "[DEBUG] >\n#{ arg.to_yaml rescue arg.pretty_inspect }"
1205
+ end
1206
+ end
1207
+
1208
+ def debug(arg)
1209
+ debug!(arg) if debug?
1210
+ end
1211
+
1212
+ def debug?
1213
+ ENV['SCRIPT_DEBUG'] || ENV['DEBUG']
1214
+ end
1215
+
1216
+ def sys!(*args, &block)
1217
+ opts = extract_options!(args)
1218
+
1219
+ cmd = args
1220
+
1221
+ debug(:cmd => cmd)
1222
+
1223
+ open3 = (
1224
+ block ||
1225
+ opts[:stdin] ||
1226
+ opts[:quiet] ||
1227
+ opts[:capture]
1228
+ )
1229
+
1230
+ if(open3)
1231
+ stdin = opts[:stdin]
1232
+ stdout = ''
1233
+ stderr = ''
1234
+ status = nil
1235
+
1236
+ Open3.popen3(*cmd) do |i, o, e, t|
1237
+ ot = async_reader_thread_for(o, stdout)
1238
+ et = async_reader_thread_for(e, stderr)
1239
+
1240
+ i.write(stdin) if stdin
1241
+ i.close
1242
+
1243
+ ot.join
1244
+ et.join
1245
+
1246
+ status = t.value
1247
+ end
1248
+
1249
+ if status.exitstatus == 0
1250
+ result = nil
1251
+
1252
+ if opts[:capture]
1253
+ result = stdout.to_s.strip
1254
+ else
1255
+ if block
1256
+ result = block.call(status, stdout, stderr)
1257
+ else
1258
+ result = [status, stdout, stderr]
1259
+ end
1260
+ end
1261
+
1262
+ return(result)
1263
+ else
1264
+ if opts[:capture]
1265
+ abort("#{ [cmd].join(' ') } #=> #{ status.exitstatus }")
1266
+ else
1267
+ false
1268
+ end
1269
+ end
1270
+ else
1271
+ env = opts[:env] || {}
1272
+ argv = [env, *cmd]
1273
+ system(*argv) || abort("#{ [cmd].join(' ') } #=> #{ $?.exitstatus }")
1274
+ return true
1275
+ end
1276
+ end
1277
+
1278
+ def sys(*args, &block)
1279
+ begin
1280
+ sys!(*args, &block)
1281
+ rescue Object
1282
+ false
1283
+ end
1284
+ end
1285
+
1286
+ def async_reader_thread_for(io, accum)
1287
+ Thread.new(io, accum) do |i, a|
1288
+ Thread.current.abort_on_exception = true
1289
+
1290
+ while true
1291
+ buf = i.read(8192)
1292
+
1293
+ if buf
1294
+ a << buf
1295
+ else
1296
+ break
1297
+ end
1298
+ end
1299
+ end
1300
+ end
1301
+
1302
+ def realpath(path)
1303
+ Pathname.new(path.to_s).expand_path.realpath.to_s
1304
+ end
1305
+
1306
+ def filelist(*args, &block)
1307
+ accum = (block || proc{ Set.new }).call
1308
+ raise ArgumentError.new('accum.class != Set') unless accum.is_a?(Set)
1309
+
1310
+ _ = args.last.is_a?(Hash) ? args.pop : {}
1311
+
1312
+ entries = args.flatten.compact.map{|arg| realpath("#{ arg }")}.uniq.sort
1313
+
1314
+ entries.each do |entry|
1315
+ case
1316
+ when test(?f, entry)
1317
+ file = realpath(entry)
1318
+ accum << file
1319
+
1320
+ when test(?d, entry)
1321
+ glob = File.join(entry, '**/**')
1322
+
1323
+ Dir.glob(glob) do |_entry|
1324
+ case
1325
+ when test(?f, _entry)
1326
+ filelist(_entry){ accum }
1327
+ when test(?d, entry)
1328
+ filelist(_entry){ accum }
1329
+ end
1330
+ end
1331
+ end
1332
+ end
1333
+
1334
+ accum.to_a
1335
+ end
1336
+
1337
+ def slug_for(*args, &block)
1338
+ Slug.for(*args, &block)
1339
+ end
1340
+
1341
+ extend Util
1342
+ end
1343
+
1344
+ def self.utils(&block)
1345
+ block ? Util.module_eval(&block) : Util
1346
+ end
1347
+
1348
+ def utils
1349
+ Util
1350
+ end
1351
+
1352
+ def u
1353
+ Util
1354
+ end
1355
+
1356
+ #
1357
+ class Slug < ::String
1358
+ Join = '-'
1359
+
1360
+ def Slug.for(*args)
1361
+ options = args.last.is_a?(Hash) ? args.pop : {}
1362
+
1363
+ join = (options[:join] || options['join'] || Join).to_s
1364
+
1365
+ string = args.flatten.compact.join(' ')
1366
+
1367
+ tokens = string.scan(%r`[^\s#{ join }]+`)
1368
+
1369
+ tokens.map! do |token|
1370
+ token.gsub(%r`[^\p{L}/.]`, '').downcase
1371
+ end
1372
+
1373
+ tokens.map! do |token|
1374
+ token.gsub(%r`[/.]`, join * 2)
1375
+ end
1376
+
1377
+ tokens.join(join)
1378
+ end
1379
+ end
1380
+
1381
+ #
1382
+ def noop
1383
+ ENV['SCRIPT_NOOP'] || ENV['NOOP']
1384
+ end
1385
+
1386
+ alias_method :noop?, :noop
1387
+
1388
+ #
1389
+ def self.help(*args)
1390
+ @help ||= nil
1391
+
1392
+ unless args.empty?
1393
+ @help = utils.unindent(args.join)
1394
+ end
1395
+
1396
+ @help
1397
+ end
1398
+
1399
+ def self.run(*args, &block)
1400
+ modes =
1401
+ if args.empty?
1402
+ [nil]
1403
+ else
1404
+ args
1405
+ end
1406
+
1407
+ modes.each do |mode|
1408
+ method_name =
1409
+ if mode
1410
+ "run_#{ mode }"
1411
+ else
1412
+ "run"
1413
+ end
1414
+
1415
+ define_method(method_name, &block)
1416
+ end
1417
+ end
1418
+
1419
+ #
1420
+ def Script.klass_for(&block)
1421
+ Class.new(Script) do |klass|
1422
+ def klass.name; "Script::Klass__#{ SecureRandom.uuid.to_s.gsub('-', '_') }"; end
1423
+ klass.class_eval(&block)
1424
+ end
1425
+ end
1426
+
1427
+ def self.run!(*args, &block)
1428
+ STDOUT.sync = true
1429
+ STDERR.sync = true
1430
+
1431
+ %w[ PIPE INT ].each{|signal| Signal.trap(signal, "EXIT")}
1432
+
1433
+ script = (
1434
+ source =
1435
+ if binding.respond_to?(:source_location)
1436
+ File.expand_path(binding.source_location.first)
1437
+ else
1438
+ File.expand_path(eval('__FILE__', block.binding))
1439
+ end
1440
+
1441
+ root = File.dirname(source)
1442
+
1443
+ klass = Script.klass_for(&block)
1444
+
1445
+ instance = klass.new
1446
+
1447
+ instance.source = source
1448
+ instance.root = root
1449
+
1450
+ instance
1451
+ )
1452
+
1453
+ script.run!(*args)
1454
+ end
1455
+ end
1456
+ end
1457
+
1458
+ #
1459
+ def Senv.script(*args, &block)
1460
+ Senv::Script.run!(*args, &block)
1461
+ end
1462
+
1463
+
1464
+ ### </lib src='lib/senv/script.rb'>
1465
+
1466
+
1467
+
1468
+ }