falsework 2.1.1 → 3.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.
- data/README.rdoc +19 -59
- data/Rakefile +28 -20
- data/bin/falsework +266 -73
- data/doc/NEWS.rdoc +24 -0
- data/doc/README.rdoc +19 -59
- data/doc/TODO.org +16 -6
- data/doc/template-tutorial.rdoc +49 -12
- data/dynamic.yaml +4 -0
- data/falsework.gemspec +10 -1
- data/lib/falsework/cliconfig.rb +14 -9
- data/lib/falsework/cliutils.rb +0 -2
- data/lib/falsework/meta.rb +2 -1
- data/lib/falsework/mould.rb +121 -192
- data/lib/falsework/upgrader.rb +120 -0
- data/lib/falsework/utils.rb +23 -0
- data/templates/c-glib/#config.yaml +18 -0
- data/{lib/falsework/templates → templates}/c-glib/README +0 -0
- data/{lib/falsework/templates → templates}/c-glib/doc/#doc.ascii +0 -0
- data/{lib/falsework/templates → templates}/c-glib/doc/%%@project%%.1.asciidoc +0 -0
- data/{lib/falsework/templates → templates}/c-glib/doc/LICENSE +0 -0
- data/{lib/falsework/templates → templates}/c-glib/doc/Makefile +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/#exe.c +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/#exe.h +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/%%@project%%.c +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/%%@project%%.h +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/Makefile +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/untest.c +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/untest.h +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/utils.c +0 -0
- data/{lib/falsework/templates → templates}/c-glib/src/utils.h +0 -0
- data/{lib/falsework/templates → templates}/c-glib/test/#test.c +0 -0
- data/{lib/falsework/templates → templates}/c-glib/test/Makefile +0 -0
- data/{lib/falsework/templates → templates}/c-glib/test/Makefile.test.mk +0 -0
- data/{lib/falsework/templates → templates}/c-glib/test/mycat.c +0 -0
- data/{lib/falsework/templates → templates}/c-glib/test/semis/text/empty.txt +0 -0
- data/{lib/falsework/templates → templates}/c-glib/test/test_utils.c +0 -0
- data/templates/ruby-cli/#config.yaml +26 -0
- data/{lib/falsework/templates → templates}/ruby-cli/%%@project%%.gemspec +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/.gitignore +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/.gitignore.#erb +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/Gemfile +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/README.rdoc +17 -19
- data/{lib/falsework/templates → templates}/ruby-cli/Rakefile +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/bin/%%@project%% +3 -3
- data/{lib/falsework/templates → templates}/ruby-cli/doc/#doc.rdoc +16 -18
- data/{lib/falsework/templates → templates}/ruby-cli/doc/LICENSE +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/doc/NEWS.rdoc +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/doc/README.rdoc +17 -19
- data/templates/ruby-cli/etc/%%@project%%.yaml +2 -0
- data/{lib/falsework/templates → templates}/ruby-cli/lib/%%@project%%/cliconfig.rb +14 -11
- data/{lib/falsework/templates → templates}/ruby-cli/lib/%%@project%%/cliutils.rb +0 -4
- data/{lib/falsework/templates → templates}/ruby-cli/lib/%%@project%%/meta.rb +1 -0
- data/{lib/falsework/templates → templates}/ruby-cli/test/helper.rb +0 -0
- data/{lib/falsework/templates → templates}/ruby-cli/test/helper_cliutils.rb +0 -3
- data/{lib/falsework/templates → templates}/ruby-cli/test/test_%%@project%%.rb +0 -0
- data/test/example/note/full +10 -0
- data/test/example/note/project-too-old +8 -0
- data/test/example/note/template-unknown +8 -0
- data/test/helper.rb +11 -0
- data/test/helper_cliutils.rb +0 -1
- data/test/templates/config-01.yaml +1 -1
- data/test/{test_exe.rb → test_cli.rb} +52 -29
- data/test/test_mould.rb +41 -16
- data/test/test_upgrader.rb +96 -0
- metadata +76 -50
- data/dynamic.ruby-cli +0 -3
- data/lib/falsework/templates/c-glib/#config.yaml +0 -18
- data/lib/falsework/templates/ruby-cli/#config.yaml +0 -15
- data/lib/falsework/templates/ruby-cli/etc/%%@project%%.yaml +0 -2
- data/test/rake_erb_templates.rb +0 -60
- data/test/templates/config-02.yaml +0 -2
- data/test/test_cl.rb +0 -33
data/lib/falsework/mould.rb
CHANGED
@@ -7,6 +7,17 @@ require 'yaml'
|
|
7
7
|
require_relative 'cliutils'
|
8
8
|
|
9
9
|
module Falsework
|
10
|
+
class MouldError < StandardError
|
11
|
+
def initialize msg
|
12
|
+
super msg
|
13
|
+
end
|
14
|
+
|
15
|
+
alias :orig_to_s :to_s
|
16
|
+
def to_s
|
17
|
+
"generator: #{orig_to_s}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
10
21
|
# The directory with template may have files beginning with # char
|
11
22
|
# which will be ignored in #project_seed (a method that creates a
|
12
23
|
# shiny new project form a template).
|
@@ -37,22 +48,27 @@ module Falsework
|
|
37
48
|
class Mould
|
38
49
|
# Where @user, @email & @gecos comes from.
|
39
50
|
GITCONFIG = '~/.gitconfig'
|
51
|
+
|
40
52
|
# The possible dirs for templates. The first one is system-wide.
|
41
|
-
|
42
|
-
|
53
|
+
@template_dirs = [CliUtils::DIR_LIB_SRC.parent.parent + 'templates',
|
54
|
+
Pathname.new(Dir.home) + ".#{Meta::NAME}" + 'templates']
|
55
|
+
class << self
|
56
|
+
attr_reader :template_dirs
|
57
|
+
end
|
58
|
+
|
43
59
|
# The template used if user didn't select one.
|
44
60
|
TEMPLATE_DEFAULT = 'ruby-cli'
|
45
61
|
# A file name with configurations for the inject commands.
|
46
62
|
TEMPLATE_CONFIG = '#config.yaml'
|
47
63
|
# A list of files to ignore in any template.
|
48
64
|
IGNORE_FILES = ['.gitignore']
|
65
|
+
# Note file name
|
66
|
+
NOTE = '.' + Meta::NAME
|
49
67
|
|
50
|
-
# A verbose level for -v CLO.
|
51
|
-
attr_accessor :verbose
|
52
|
-
# -b CLO.
|
53
|
-
attr_accessor :batch
|
54
68
|
# A directory of a new generated project.
|
55
69
|
attr_reader :project
|
70
|
+
# template configuration
|
71
|
+
attr_reader :conf
|
56
72
|
|
57
73
|
# [project] A name of the future project; may include all crap with spaces.
|
58
74
|
# [template] A name of the template for the project.
|
@@ -61,34 +77,33 @@ module Falsework
|
|
61
77
|
# [gecos] A full author name from ~/.gitconfig.
|
62
78
|
def initialize(project, template, user = nil, email = nil, gecos = nil)
|
63
79
|
@project = Mould.name_project project
|
64
|
-
|
80
|
+
fail MouldError, "invalid project name '#{project}'" unless Mould.name_valid?(@project)
|
65
81
|
@camelcase = Mould.name_camelcase project
|
66
82
|
@classy = Mould.name_classy project
|
67
83
|
|
68
|
-
@verbose = false
|
69
84
|
@batch = false
|
70
|
-
@template = template
|
71
|
-
@dir_t = Mould.templates[@template
|
85
|
+
@template = template || TEMPLATE_DEFAULT
|
86
|
+
@dir_t = Mould.templates[@template] || fail(MouldError, "template '#{@template}' not found")
|
72
87
|
|
73
88
|
# default config
|
74
89
|
@conf = {
|
75
|
-
exe
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
doc
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
test
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
+
'exe' => [{
|
91
|
+
'src' => nil,
|
92
|
+
'dest' => 'bin/%s',
|
93
|
+
'mode_int' => 0744
|
94
|
+
}],
|
95
|
+
'doc' => [{
|
96
|
+
'src' => nil,
|
97
|
+
'dest' => 'doc/%s.rdoc',
|
98
|
+
'mode_int' => nil
|
99
|
+
}],
|
100
|
+
'test' => [{
|
101
|
+
'src' => nil,
|
102
|
+
'dir' => 'test/test_%s.rb',
|
103
|
+
'mode_int' => nil
|
104
|
+
}]
|
90
105
|
}
|
91
|
-
|
106
|
+
configParse
|
92
107
|
|
93
108
|
gc = Git.global_config rescue gc = {}
|
94
109
|
@user = user || gc['github.user']
|
@@ -98,7 +113,7 @@ module Falsework
|
|
98
113
|
[['github.user', @user],
|
99
114
|
['user.email', @email],
|
100
115
|
['user.name', @gecos]].each {|i|
|
101
|
-
|
116
|
+
fail MouldError, "missing #{i.first} in #{GITCONFIG}" if i.last.to_s == ''
|
102
117
|
}
|
103
118
|
end
|
104
119
|
|
@@ -112,7 +127,7 @@ module Falsework
|
|
112
127
|
if ! File.directory?(idx)
|
113
128
|
CliUtils.warnx "invalid additional template directory: #{idx}"
|
114
129
|
else
|
115
|
-
|
130
|
+
@template_dirs << idx
|
116
131
|
end
|
117
132
|
}
|
118
133
|
end
|
@@ -169,9 +184,9 @@ module Falsework
|
|
169
184
|
# and corresponding directories.
|
170
185
|
def self.templates
|
171
186
|
r = {}
|
172
|
-
|
187
|
+
@template_dirs.each {|i|
|
173
188
|
Dir.glob(i + '*').each {|j|
|
174
|
-
r[File.basename(j)] = j if File.directory?(j)
|
189
|
+
r[File.basename(j)] = Pathname.new(j) if File.directory?(j)
|
175
190
|
}
|
176
191
|
}
|
177
192
|
r
|
@@ -180,35 +195,35 @@ module Falsework
|
|
180
195
|
# Generate a new project in @project directory from @template.
|
181
196
|
#
|
182
197
|
# Return false if nothing was extracted.
|
183
|
-
def project_seed
|
198
|
+
def project_seed
|
184
199
|
uuid = Mould.uuidgen_fake # useful variable for the template
|
185
200
|
|
186
201
|
# check for existing project
|
187
|
-
|
202
|
+
fail MouldError, "directory '#{@project}' is not empty" if Dir.glob(@project + '/*').size > 0
|
188
203
|
|
189
204
|
Dir.mkdir @project unless File.directory?(@project)
|
190
|
-
|
205
|
+
CliUtils.veputs 1, "Project path: #{File.expand_path(@project)}"
|
191
206
|
|
192
207
|
r = false
|
193
|
-
|
208
|
+
CliUtils.veputs 1, "Template: #{@dir_t}"
|
194
209
|
symlinks = []
|
195
210
|
Dir.chdir(@project) {
|
196
|
-
Mould.traverse(@dir_t) {|idx|
|
211
|
+
Mould.traverse(@dir_t.to_s) {|idx|
|
197
212
|
file = idx.sub(/^#{@dir_t}\//, '')
|
198
213
|
next if IGNORE_FILES.index {|i| file.match(/#{i}$/) }
|
199
214
|
|
200
215
|
if File.symlink?(idx)
|
201
216
|
# we'll process them later on
|
202
|
-
is_dir = File.directory?(@dir_t + '/' + File.readlink(idx))
|
203
|
-
symlinks << [Mould.
|
204
|
-
Mould.
|
217
|
+
# is_dir = File.directory?(@dir_t + '/' + File.readlink(idx))
|
218
|
+
symlinks << [Mould.resolve_filename(File.readlink(idx), getBinding),
|
219
|
+
Mould.resolve_filename(file, getBinding)]
|
205
220
|
elsif File.directory?(idx)
|
206
|
-
|
207
|
-
Dir.mkdir Mould.
|
221
|
+
CliUtils.veputs 1, "D: #{file}"
|
222
|
+
Dir.mkdir Mould.resolve_filename(file, getBinding)
|
208
223
|
else
|
209
|
-
|
210
|
-
to = Mould.
|
211
|
-
Mould.extract
|
224
|
+
CliUtils.veputs 1, "N: #{file}"
|
225
|
+
to = Mould.resolve_filename file, getBinding
|
226
|
+
Mould.extract idx, binding, to # 'binding' to include local uuid
|
212
227
|
end
|
213
228
|
r = true
|
214
229
|
}
|
@@ -217,8 +232,8 @@ module Falsework
|
|
217
232
|
symlinks.each {|idx|
|
218
233
|
src = idx[0]
|
219
234
|
dest = idx[1]
|
220
|
-
|
221
|
-
File.symlink
|
235
|
+
CliUtils.veputs 1, "L: #{dest} => #{src}"
|
236
|
+
File.symlink src, dest
|
222
237
|
}
|
223
238
|
}
|
224
239
|
|
@@ -227,33 +242,33 @@ module Falsework
|
|
227
242
|
|
228
243
|
# Parse a config. Return false on error.
|
229
244
|
#
|
230
|
-
# [
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
245
|
+
# [rvars] A list of variable names which must be in the config.
|
246
|
+
def configParse rvars = []
|
247
|
+
r = false
|
248
|
+
|
249
|
+
file = @dir_t + TEMPLATE_CONFIG
|
236
250
|
if File.readable?(file)
|
237
251
|
begin
|
238
|
-
myconf = YAML.load_file
|
252
|
+
myconf = YAML.load_file file
|
253
|
+
myconf[:file] = file
|
254
|
+
myconf['version'] = '1.0.0' unless myconf['version']
|
255
|
+
r = true
|
239
256
|
rescue
|
240
257
|
CliUtils.warnx "cannot parse #{file}: #{$!}"
|
241
258
|
return false
|
242
259
|
end
|
243
260
|
rvars.each { |i|
|
244
|
-
CliUtils.warnx "missing or nil '#{i}' in #{file}" if ! myconf.key?(i
|
245
|
-
|
261
|
+
CliUtils.warnx "missing or nil '#{i}' in #{file}" if ! myconf.key?(i) || ! myconf[i]
|
262
|
+
return false
|
246
263
|
}
|
247
|
-
|
248
|
-
|
249
|
-
else
|
250
|
-
r = false
|
264
|
+
|
265
|
+
@conf.merge!(myconf) if r
|
251
266
|
end
|
252
267
|
|
253
268
|
r
|
254
269
|
end
|
255
270
|
|
256
|
-
# Add
|
271
|
+
# Add a file from the template.
|
257
272
|
#
|
258
273
|
# [mode] Is either 'exe', 'doc' or 'test'.
|
259
274
|
# [target] A test/doc/exe file to create.
|
@@ -269,21 +284,25 @@ module Falsework
|
|
269
284
|
def add(mode, target)
|
270
285
|
target_orig = target
|
271
286
|
target = Mould.name_project target_orig
|
272
|
-
|
287
|
+
fail MouldError, "invalid target name '#{target_orig}'" if !Mould.name_valid? target
|
273
288
|
target_camelcase = Mould.name_camelcase target_orig
|
274
289
|
target_classy = Mould.name_classy target_orig
|
275
290
|
uuid = Mould.uuidgen_fake
|
276
291
|
|
277
292
|
created = []
|
278
293
|
|
279
|
-
|
294
|
+
unless @conf[mode] && @conf[mode][0]['src']
|
295
|
+
CliUtils.warnx "hash '#{mode}' is empty in #{@conf[:file]}"
|
296
|
+
return []
|
297
|
+
end
|
280
298
|
|
281
|
-
@conf[mode
|
282
|
-
to = idx[
|
299
|
+
@conf[mode].each {|idx|
|
300
|
+
to = idx['dest'] % target
|
283
301
|
|
284
302
|
begin
|
285
|
-
|
286
|
-
|
303
|
+
# 'binding' to include local vars
|
304
|
+
Mould.extract @dir_t + idx['src'], binding, to
|
305
|
+
File.chmod(idx['mode_int'], to) if idx['mode_int']
|
287
306
|
rescue
|
288
307
|
CliUtils.warnx "failed to create '#{to}' (check your #config.yaml): #{$!}"
|
289
308
|
else
|
@@ -315,159 +334,69 @@ module Falsework
|
|
315
334
|
|
316
335
|
# Extract file @from into @to.
|
317
336
|
#
|
318
|
-
# [
|
319
|
-
def self.extract
|
320
|
-
t = ERB.new
|
321
|
-
t.filename = from # to report errors relative to this file
|
337
|
+
# [bng] A binding for eval.
|
338
|
+
def self.extract from, bng, to
|
339
|
+
t = ERB.new File.read(from.to_s)
|
340
|
+
t.filename = from.to_s # to report errors relative to this file
|
322
341
|
begin
|
323
|
-
output = t.result
|
342
|
+
output = t.result bng
|
324
343
|
md5_system = Digest::MD5.hexdigest(output)
|
325
344
|
rescue Exception
|
326
|
-
|
345
|
+
fail MouldError, "bogus template file '#{from}': #{$!}"
|
327
346
|
end
|
328
347
|
|
329
|
-
|
348
|
+
unless File.exists?(to)
|
330
349
|
# write a skeleton
|
331
350
|
begin
|
351
|
+
FileUtils.mkdir_p File.dirname(to)
|
332
352
|
File.open(to, 'w+') { |fp| fp.puts output }
|
333
353
|
# transfer the exec bit to the generated result
|
334
|
-
File.chmod(0744, to) if File.stat(from).executable?
|
354
|
+
File.chmod(0744, to) if !defined?(FakeFS) && File.stat(from.to_s).executable?
|
335
355
|
rescue
|
336
|
-
|
356
|
+
fail MouldError, "cannot generate: #{$!}"
|
337
357
|
end
|
338
|
-
|
358
|
+
else
|
339
359
|
# warn a careless user
|
340
|
-
if md5_system != Digest::MD5.file(to).hexdigest
|
341
|
-
CliUtils.errx(1, "'#{to}' already exists")
|
342
|
-
end
|
360
|
+
CliUtils.warnx "'#{to}' already exists in modified state" if md5_system != Digest::MD5.file(to).hexdigest
|
343
361
|
end
|
344
362
|
end
|
345
363
|
|
346
|
-
# Resolve
|
347
|
-
def self.
|
364
|
+
# Resolve t from possible %%VARIABLE%% scheme.
|
365
|
+
def self.resolve_filename t, bng
|
348
366
|
t || (return '')
|
349
367
|
|
350
368
|
re = /%%([^%]+)%%/
|
351
|
-
t = ERB.new(t.gsub(re, '<%= \+ %>')).result(
|
369
|
+
t = ERB.new(t.gsub(re, '<%= \+ %>')).result(bng) if t =~ re
|
352
370
|
t.sub(/\.#erb$/, '')
|
353
371
|
end
|
354
|
-
|
355
372
|
|
356
|
-
|
357
|
-
|
358
|
-
# /^..? :erb:/
|
359
|
-
#
|
360
|
-
# in first n lines. If the line is found, the file is considered a
|
361
|
-
# candidate for an upgrade. Return a hash {target:template}
|
362
|
-
def upgradable_files()
|
363
|
-
line_max = 4
|
364
|
-
r = {}
|
365
|
-
Mould.traverse(@dir_t) {|i|
|
366
|
-
next if File.directory?(i)
|
367
|
-
next if File.symlink?(i) # hm...
|
368
|
-
|
369
|
-
File.open(i) {|fp|
|
370
|
-
n = 0
|
371
|
-
while n < line_max && line = fp.gets
|
372
|
-
if line =~ /^..? :erb:/
|
373
|
-
t = i.sub(/#{@dir_t}\//, '')
|
374
|
-
r[Mould.get_filename(t, binding)] = i
|
375
|
-
break
|
376
|
-
end
|
377
|
-
n += 1
|
378
|
-
end
|
379
|
-
}
|
380
|
-
}
|
381
|
-
|
382
|
-
r
|
373
|
+
def getBinding
|
374
|
+
binding
|
383
375
|
end
|
384
376
|
|
385
|
-
#
|
386
|
-
#
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
# files. But if there is at least 1 file from a newer version of
|
398
|
-
# falsework then no upgrade is possible--it's considered a user
|
399
|
-
# decision to intentionally have some files from the old versions of
|
400
|
-
# falsework.
|
401
|
-
#
|
402
|
-
# Neithe we do check for a content of upgradable files nor try to
|
403
|
-
# merge old with new. (Why?)
|
404
|
-
def upgrade()
|
405
|
-
# 0. search for 'new' files in the template
|
406
|
-
uf = upgradable_files
|
407
|
-
fail "template #{@template} cannot offer you files for the upgrade" if uf.size == 0
|
408
|
-
# pp uf
|
409
|
-
|
410
|
-
# 1. analyse 'old' files
|
411
|
-
u = {}
|
412
|
-
uf.each {|k, v|
|
413
|
-
if ! File.readable?(k)
|
414
|
-
u[k] = v
|
415
|
-
else
|
416
|
-
# check for its version
|
417
|
-
File.open(k) {|fp|
|
418
|
-
is_versioned = false
|
419
|
-
while line = fp.gets
|
420
|
-
if line =~ /^# Don't remove this: falsework\/(#{Gem::Version::VERSION_PATTERN})\/(.+)\/.+/
|
421
|
-
is_versioned = true
|
422
|
-
if $3 != (@template || TEMPLATE_DEFAULT)
|
423
|
-
fail "file #{k} is from '#{$3}' template"
|
424
|
-
end
|
425
|
-
if Gem::Version.new(Meta::VERSION) >= Gem::Version.new($1)
|
426
|
-
# puts "#{k}: #{$1}"
|
427
|
-
u[k] = v
|
428
|
-
break
|
429
|
-
else
|
430
|
-
fail "file #{k} is from a newer version of #{Meta::NAME}: " + $1
|
431
|
-
end
|
432
|
-
end
|
433
|
-
end
|
434
|
-
|
435
|
-
CliUtils.warnx("#{k}: unversioned") if ! is_versioned
|
436
|
-
}
|
437
|
-
end
|
377
|
+
# Write a YAML file into a created project directory. This file is
|
378
|
+
# required for Upgrader.
|
379
|
+
def noteCreate after_upgrade = false
|
380
|
+
h = {
|
381
|
+
'project' => {
|
382
|
+
'classy' => @classy,
|
383
|
+
'upgraded' => DateTime.now.iso8601
|
384
|
+
},
|
385
|
+
'template' => {
|
386
|
+
'version' => @conf['version'],
|
387
|
+
'name' => @template
|
388
|
+
}
|
438
389
|
}
|
439
|
-
fail "template #{@template || TEMPLATE_DEFAULT} cannot find files for an upgrade" if u.size == 0
|
440
|
-
|
441
|
-
# 2. ask user for a commitment
|
442
|
-
if ! @batch
|
443
|
-
puts "Here is a list of files in project #{@project} we can try to upgrade/add:\n\n"
|
444
|
-
u.each {|k,v| puts "\t#{k}"}
|
445
|
-
printf %{
|
446
|
-
Does this look fine? Type y/n and press enter. If you choose 'y', those files
|
447
|
-
will be replaced with newer versions. Your old files will be preserved with
|
448
|
-
an '.old' extension. So? }
|
449
|
-
if STDIN.gets =~ /^y/i
|
450
|
-
puts ""
|
451
|
-
else
|
452
|
-
puts "\nNo? See you later."
|
453
|
-
exit 0
|
454
|
-
end
|
455
|
-
end
|
456
390
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
File.rename(k, "#{k}.old") rescue CliUtils.warnx('renaming failed')
|
465
|
-
printf("%#{tsl}s Extracting %s ...\n", "", File.basename(v)) if @verbose
|
466
|
-
FileUtils.mkdir_p(File.dirname(k))
|
467
|
-
Mould.extract(v, binding, k)
|
468
|
-
count += 1
|
391
|
+
file = @project + '/' + NOTE
|
392
|
+
file = NOTE if after_upgrade # in 'upgrade' mode we are in project dir
|
393
|
+
File.open(file, 'w+') {|fp|
|
394
|
+
CliUtils.veputs 1, "N: #{File.basename(file)}"
|
395
|
+
fp.puts "# DO NOT DELETE THIS FILE"
|
396
|
+
fp.puts "# unless you don't want to upgrade scaffolds in the future."
|
397
|
+
fp.puts h.to_yaml
|
469
398
|
}
|
470
399
|
end
|
471
400
|
|
472
|
-
end
|
401
|
+
end
|
473
402
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require_relative 'mould'
|
4
|
+
require_relative 'utils'
|
5
|
+
|
6
|
+
module Falsework
|
7
|
+
|
8
|
+
class UpgradeError < StandardError
|
9
|
+
def initialize msg
|
10
|
+
super msg
|
11
|
+
end
|
12
|
+
|
13
|
+
alias :orig_to_s :to_s
|
14
|
+
def to_s
|
15
|
+
"upgrade: #{orig_to_s}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Upgrader
|
20
|
+
def self.noteLoad file = Mould::NOTE
|
21
|
+
r = YAML.load_file(file) rescue raise
|
22
|
+
|
23
|
+
['project', 'template'].each {|idx|
|
24
|
+
fail "no #{idx} spec" unless r[idx]
|
25
|
+
}
|
26
|
+
|
27
|
+
fail 'no project name' unless Utils.all_set?(r['project']['classy'])
|
28
|
+
fail "no template version" unless Utils.all_set?(r['template']['version'])
|
29
|
+
r['template']['version'] = Gem::Version.new r['template']['version']
|
30
|
+
fail "no template name" unless Utils.all_set?(r['template']['name'])
|
31
|
+
|
32
|
+
unless Mould.templates[r['template']['name']]
|
33
|
+
fail "unknown template '#{r['template']['name']}'"
|
34
|
+
end
|
35
|
+
|
36
|
+
r
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize dir, note = Mould::NOTE
|
40
|
+
fail UpgradeError, "directory #{dir} is unreadable" unless File.readable?(dir.to_s)
|
41
|
+
@dir = Pathname.new File.realpath(dir)
|
42
|
+
|
43
|
+
begin
|
44
|
+
@note = Upgrader.noteLoad(@dir + note)
|
45
|
+
rescue
|
46
|
+
raise UpgradeError, $!
|
47
|
+
end
|
48
|
+
|
49
|
+
@mould = Mould.new @note['project']['classy'], @note['template']['name']
|
50
|
+
@template_dir = Mould.templates[@note['template']['name']]
|
51
|
+
@project = @mould.project
|
52
|
+
|
53
|
+
@batch = false
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_accessor :batch
|
57
|
+
attr_reader :project
|
58
|
+
attr_reader :template_dir
|
59
|
+
|
60
|
+
def getProjectBinding
|
61
|
+
@mould.getBinding
|
62
|
+
end
|
63
|
+
|
64
|
+
def able?
|
65
|
+
return false unless @mould.conf['upgrade']
|
66
|
+
return false if Gem::Version.new(@mould.conf['upgrade']['from']) > @note['template']['version']
|
67
|
+
return false unless @mould.conf['upgrade']['files'].is_a?(Array) && @mould.conf['upgrade']['files'].size > 0
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def files
|
72
|
+
@mould.conf['upgrade']['files']
|
73
|
+
end
|
74
|
+
|
75
|
+
def obsolete
|
76
|
+
@mould.conf['upgrade']['obsolete'] || []
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return true if user enter 'y' or 'a', false otherwise.
|
80
|
+
# Always return true for non-batch mode.
|
81
|
+
def userSaidYes msg, file
|
82
|
+
return true if @batch
|
83
|
+
|
84
|
+
print "#{msg} '#{file}'? [y/n/a] "
|
85
|
+
asnwer = $stdin.gets
|
86
|
+
|
87
|
+
if asnwer =~ /^a/i
|
88
|
+
@batch = true
|
89
|
+
return true
|
90
|
+
end
|
91
|
+
|
92
|
+
yes = (asnwer =~ /^y/i)
|
93
|
+
return true if yes
|
94
|
+
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
def upgrade save_old = false # yield f
|
99
|
+
fail UpgradeError, "this project cannot be upgraded" unless able?
|
100
|
+
|
101
|
+
at_least_one = false
|
102
|
+
files.each {|idx|
|
103
|
+
f = Mould.resolve_filename idx, @mould.getBinding
|
104
|
+
|
105
|
+
next unless userSaidYes 'update', f
|
106
|
+
|
107
|
+
FileUtils.mv f, f + '.orig' if save_old && File.exist?(f)
|
108
|
+
Mould.extract @template_dir + idx, @mould.getBinding, f
|
109
|
+
|
110
|
+
# say 'opa-popa was updated'
|
111
|
+
yield f if block_given?
|
112
|
+
at_least_one = true
|
113
|
+
}
|
114
|
+
|
115
|
+
# update a note file
|
116
|
+
@mould.noteCreate true if at_least_one
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Falsework
|
2
|
+
module Utils
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def self.all_set? t
|
6
|
+
return false unless t
|
7
|
+
|
8
|
+
if t.is_a?(Array)
|
9
|
+
return false if t.size == 0
|
10
|
+
|
11
|
+
t.each {|i|
|
12
|
+
return false unless i
|
13
|
+
return false if i.to_s.strip.size == 0
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
return false if t.to_s.strip.size == 0
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
exe:
|
3
|
+
- src: 'src/#exe.h'
|
4
|
+
dest: 'src/%s.h'
|
5
|
+
mode_int: null
|
6
|
+
- src: 'src/#exe.c'
|
7
|
+
dest: 'src/%s.c'
|
8
|
+
mode_int: null
|
9
|
+
|
10
|
+
doc:
|
11
|
+
- src: 'doc/#doc.ascii'
|
12
|
+
dest: 'doc/%s.1.asciidoc'
|
13
|
+
mode_int: null
|
14
|
+
|
15
|
+
test:
|
16
|
+
- src: 'test/#test.c'
|
17
|
+
dest: 'test/test_%s.c'
|
18
|
+
mode_int: null
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|