ascii_binder 0.1.9 → 0.1.10

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -0
  3. data/ascii_binder.gemspec +5 -3
  4. data/bin/asciibinder +53 -44
  5. data/features/command_help.feature +8 -0
  6. data/features/command_version.feature +8 -0
  7. data/features/repo_build.feature +34 -0
  8. data/features/repo_clean.feature +20 -0
  9. data/features/repo_clone.feature +22 -0
  10. data/features/repo_create.feature +13 -0
  11. data/features/repo_package.feature +15 -0
  12. data/features/step_definitions/steps.rb +182 -0
  13. data/features/support/_clone_distro_map.yml +14 -0
  14. data/features/support/_invalid_distro_map.yml +14 -0
  15. data/features/support/env.rb +468 -0
  16. data/features/support/test_distro/.gitignore +9 -0
  17. data/features/support/test_distro/_distro_map.yml +29 -0
  18. data/features/support/test_distro/_images/asciibinder-logo-horizontal.png +0 -0
  19. data/features/support/test_distro/_images/asciibinder_web_logo.svg +125 -0
  20. data/features/support/test_distro/_images/book_pages_bg.jpg +0 -0
  21. data/features/support/test_distro/_images/favicon.ico +0 -0
  22. data/features/support/test_distro/_images/favicon32x32.png +0 -0
  23. data/features/support/test_distro/_javascripts/.gitkeep +0 -0
  24. data/features/support/test_distro/_stylesheets/asciibinder.css +568 -0
  25. data/features/support/test_distro/_templates/_css.html.erb +3 -0
  26. data/features/support/test_distro/_templates/_nav.html.erb +31 -0
  27. data/features/support/test_distro/_templates/page.html.erb +92 -0
  28. data/features/support/test_distro/_topic_map.yml +36 -0
  29. data/features/support/test_distro/index-main.html +10 -0
  30. data/features/support/test_distro/index-test.html +10 -0
  31. data/features/support/test_distro/main_only_topic_group/index.adoc +17 -0
  32. data/features/support/test_distro/test_only_topic_group/index.adoc +17 -0
  33. data/features/support/test_distro/welcome/index.adoc +17 -0
  34. data/features/support/test_distro/welcome/subtopics/index.adoc +17 -0
  35. data/features/support/test_distro/welcome/subtopics/main_only_topic.adoc +17 -0
  36. data/features/support/test_distro/welcome/subtopics/test_only_topic.adoc +17 -0
  37. data/features/support/test_distro/welcome/subtopics/wildcard_all.adoc +17 -0
  38. data/lib/ascii_binder.rb +4 -2
  39. data/lib/ascii_binder/distro.rb +111 -0
  40. data/lib/ascii_binder/distro_branch.rb +93 -0
  41. data/lib/ascii_binder/distro_map.rb +67 -0
  42. data/lib/ascii_binder/engine.rb +581 -0
  43. data/lib/ascii_binder/helpers.rb +42 -856
  44. data/lib/ascii_binder/site.rb +52 -0
  45. data/lib/ascii_binder/site_info.rb +22 -0
  46. data/lib/ascii_binder/site_map.rb +24 -0
  47. data/lib/ascii_binder/topic_entity.rb +255 -0
  48. data/lib/ascii_binder/topic_map.rb +61 -0
  49. data/lib/ascii_binder/version.rb +1 -1
  50. data/templates/_templates/page.html.erb +1 -1
  51. metadata +118 -14
@@ -0,0 +1,14 @@
1
+ ---
2
+ ascii_binder:
3
+ name: AsciiBinder Doc Project
4
+ author: AsciiBinder Team <team@asciibinder.org>
5
+ site: main
6
+ site_name: Home
7
+ site_url: http://asciibinder.org/
8
+ branches:
9
+ master:
10
+ name: Latest
11
+ dir: latest
12
+ branch1:
13
+ name: Branch 1
14
+ dir: branch1
@@ -0,0 +1,14 @@
1
+ ---
2
+ ascii_binder:
3
+ name: " "
4
+ author: " "
5
+ site: " "
6
+ site_name: " "
7
+ site_url: " "
8
+ branches:
9
+ master:
10
+ name: " "
11
+ dir: " "
12
+ branch1:
13
+ name: " "
14
+ dir: " "
@@ -0,0 +1,468 @@
1
+ require 'fileutils'
2
+ require 'git'
3
+ require 'open3'
4
+ require 'tmpdir'
5
+ require 'yaml'
6
+
7
+ module Helpers
8
+ def gem_root
9
+ File.expand_path '../../..', __FILE__
10
+ end
11
+
12
+ def run_command(command,args=[],repo_dir=nil)
13
+ if repo_dir.nil?
14
+ repo_dir = working_dir
15
+ end
16
+ instructions = [File.join(gem_root,'bin','asciibinder'),command]
17
+ instructions.concat(args)
18
+ instructions << repo_dir
19
+ stdout_str, stderr_str, status = Open3.capture3(instructions.join(' '))
20
+ return { :stdout => stdout_str, :stderr => stderr_str, :status => status }
21
+ end
22
+
23
+ def print_output(command_output)
24
+ puts "STDOUT:\n#{command_output[:stdout]}\n\n"
25
+ puts "STDERR:\n#{command_output[:stderr]}\n\n"
26
+ puts "EXIT CODE: #{command_output[:status].exitstatus}\n\n"
27
+ end
28
+
29
+ def working_dir
30
+ @working_dir ||= begin
31
+ working_dir = Dir.mktmpdir('ascii_binder-cucumber')
32
+ track_tmp_dir(working_dir)
33
+ FileUtils.rm_rf(working_dir)
34
+ working_dir
35
+ end
36
+ end
37
+
38
+ def distro_map
39
+ @distro_map ||= YAML.load_file(File.join(docs_root,'_distro_map.yml'))
40
+ end
41
+
42
+ def topic_map
43
+ # Normally we want to read the topic map from each branch. In our test setup,
44
+ # each branch has an identical topic map, so we can get away with this for now.
45
+ @topic_map ||= YAML.load_stream(open(File.join(docs_root,'_topic_map.yml')))
46
+ end
47
+
48
+ def preview_dir
49
+ @preview_dir ||= File.join(docs_root,'_preview')
50
+ end
51
+
52
+ def package_dir
53
+ @package_dir ||= File.join(docs_root,'_package')
54
+ end
55
+
56
+ def repo_template_dir
57
+ @repo_template_dir ||= File.join(gem_root,'templates')
58
+ end
59
+
60
+ def test_distro_dir
61
+ @test_distro_dir ||= File.join(gem_root,'features','support','test_distro')
62
+ end
63
+
64
+ def track_tmp_dir(tmp_dir)
65
+ if @tracked_tmp_dirs.nil?
66
+ @tracked_tmp_dirs = []
67
+ end
68
+ @tracked_tmp_dirs << tmp_dir unless @tracked_tmp_dirs.include?(tmp_dir)
69
+ end
70
+
71
+ def clean_tracked_dirs
72
+ @tracked_tmp_dirs.each do |dir|
73
+ FileUtils.rm_rf(dir)
74
+ end
75
+ end
76
+
77
+ def find_html_files(dir)
78
+ `cd #{dir} && find .`.split("\n").select{ |item| item.end_with?('.html') }.map{ |item| item[2..-1] }
79
+ end
80
+
81
+ def actual_preview_info
82
+ all_preview_paths = find_html_files(preview_dir)
83
+
84
+ map = {}
85
+ dirmatch = {}
86
+ distro_map.each do |distro,distro_info|
87
+ map[distro] = {}
88
+ distro_info['branches'].each do |branch,branch_info|
89
+ map[distro][branch] = []
90
+ dirmatch["#{distro}/#{branch_info['dir']}"] = { :distro => distro, :branch => branch }
91
+ end
92
+ end
93
+
94
+ populated_distros = []
95
+ populated_branches = []
96
+ populated_pages = []
97
+ all_preview_paths.each do |preview_path|
98
+ found_dirmatch = false
99
+ dirmatch.each do |branch_path,db_keys|
100
+ next unless preview_path.start_with?(branch_path)
101
+ found_dirmatch = true
102
+ map[db_keys[:distro]][db_keys[:branch]] << preview_path
103
+ populated_distros << db_keys[:distro]
104
+ populated_branches << db_keys[:branch]
105
+ populated_pages << preview_path.split('/')[2..-1].join('/')
106
+ break
107
+ end
108
+ unless found_dirmatch
109
+ puts "ERROR: unexpected output file '#{preview_path}'"
110
+ exit 1
111
+ end
112
+ end
113
+
114
+ map.keys.each do |distro|
115
+ map[distro].keys.each do |branch|
116
+ map[distro][branch].sort!
117
+ end
118
+ end
119
+
120
+ return {
121
+ :map => map,
122
+ :distros => populated_distros.uniq,
123
+ :branches => populated_branches.uniq,
124
+ :pages => populated_pages.uniq,
125
+ }
126
+ end
127
+
128
+ def actual_site_map
129
+ all_site_paths = find_html_files(package_dir)
130
+
131
+ map = {}
132
+ dirmatch = {}
133
+ distro_map.each do |distro,distro_info|
134
+ site = distro_info['site']
135
+ unless map.has_key?(site)
136
+ map[site] = {}
137
+ end
138
+ map[site][distro] = {}
139
+ distro_info['branches'].each do |branch,branch_info|
140
+ map[site][distro][branch] = []
141
+ dirmatch["#{distro_info['site']}/#{branch_info['dir']}"] = {
142
+ :distro => distro,
143
+ :branch => branch,
144
+ :site => site,
145
+ }
146
+ end
147
+ end
148
+
149
+ all_site_paths.each do |site_path|
150
+ # skip the top-level index.html file in each site.
151
+ path_parts = site_path.split('/')
152
+ next if path_parts.length == 2 and path_parts[1] == 'index.html'
153
+
154
+ found_dirmatch = false
155
+ dirmatch.each do |branch_path,db_keys|
156
+ next unless site_path.start_with?(branch_path)
157
+ found_dirmatch = true
158
+ map[db_keys[:site]][db_keys[:distro]][db_keys[:branch]] << site_path
159
+ break
160
+ end
161
+ unless found_dirmatch
162
+ puts "ERROR: unexpected output file '#{site_path}'"
163
+ exit 1
164
+ end
165
+ end
166
+
167
+ map.keys.each do |site|
168
+ map[site].keys.each do |distro|
169
+ map[site][distro].keys.each do |branch|
170
+ map[site][distro][branch].sort!
171
+ end
172
+ end
173
+ end
174
+
175
+ return map
176
+ end
177
+
178
+ def distro_preview_path_map
179
+ map = {}
180
+ distro_map.each do |distro,distro_info|
181
+ map[distro] = {}
182
+ distro_info['branches'].each do |branch,branch_info|
183
+ map[distro][branch] = []
184
+ topic_map.each do |topic_node|
185
+ map[distro][branch].concat(topic_paths(distro,topic_node).map{ |subpath| "#{distro}/#{branch_info['dir']}/#{subpath}" })
186
+ end
187
+ map[distro][branch].sort!
188
+ end
189
+ end
190
+ return map
191
+ end
192
+
193
+ def distro_site_path_map
194
+ map = {}
195
+ distro_map.each do |distro,distro_info|
196
+ site = distro_info['site']
197
+ unless map.has_key?(site)
198
+ map[site] = {}
199
+ end
200
+ map[site][distro] = {}
201
+ distro_info['branches'].each do |branch,branch_info|
202
+ map[site][distro][branch] = []
203
+ topic_map.each do |topic_node|
204
+ map[site][distro][branch].concat(topic_paths(distro,topic_node).map{ |subpath| "#{site}/#{branch_info['dir']}/#{subpath}" })
205
+ end
206
+ map[site][distro][branch].sort!
207
+ end
208
+ end
209
+ return map
210
+ end
211
+
212
+ def topic_paths(distro,topic_node)
213
+ # First, determine if this topic node should be included for this distro.
214
+ if topic_node.has_key?('Distros')
215
+ found_distro = false
216
+ included_distros = topic_node['Distros'].split(',')
217
+ included_distros.each do |check_distro|
218
+ if check_distro.include?('*') and File.fnmatch(check_distro,distro)
219
+ found_distro = true
220
+ break
221
+ elsif check_distro == distro
222
+ found_distro = true
223
+ break
224
+ end
225
+ end
226
+ unless found_distro
227
+ return []
228
+ end
229
+ end
230
+
231
+ if topic_node.has_key?('File')
232
+ # This topic node is a topic "leaf"; return it with '.html' as the extension.
233
+ filename = topic_node['File'].split('.')[0]
234
+ return ["#{filename}.html"]
235
+ elsif topic_node.has_key?('Dir')
236
+ dirpath = topic_node['Dir']
237
+ subtopics = []
238
+ topic_node['Topics'].each do |subtopic_node|
239
+ subtopics.concat(topic_paths(distro,subtopic_node))
240
+ end
241
+ return subtopics.map{ |subpath| "#{dirpath}/#{subpath}" }
242
+ else
243
+ puts "ERROR: Malformed topic node. #{topic_node.inspect}"
244
+ exit 1
245
+ end
246
+ end
247
+
248
+ def set_initial_working_branch(branch)
249
+ @initial_working_branch = branch
250
+ end
251
+
252
+ def initial_working_branch
253
+ @initial_working_branch ||= nil
254
+ end
255
+
256
+ def using_offset_docs_root?
257
+ @using_offset_docs_root
258
+ end
259
+
260
+ def docs_root
261
+ using_offset_docs_root? ? File.join(working_dir,'docs') : working_dir
262
+ end
263
+
264
+ def initialize_test_repo(valid,multiple_distros=false,offset_docs_root=false)
265
+ unless valid
266
+ FileUtils.mkdir(working_dir)
267
+ else
268
+ status_check(run_command('create'),'Could not initialize test repo.')
269
+ if multiple_distros
270
+ FileUtils.cp_r(File.join(test_distro_dir,'.'),working_dir)
271
+ end
272
+ if offset_docs_root
273
+ @using_offset_docs_root = true
274
+ entries = Dir.entries(working_dir).select{ |item| not item.start_with?('.') }
275
+ system("cd #{working_dir} && mkdir docs")
276
+ entries.each do |entry|
277
+ system("cd #{working_dir} && mv #{entry} docs")
278
+ end
279
+ end
280
+ system("cd #{working_dir} && git add . > /dev/null && git commit -am 'test commit' > /dev/null")
281
+ if multiple_distros
282
+ system("cd #{working_dir} && git checkout -b branch1 > /dev/null 2>&1 && git checkout -b branch2 > /dev/null 2>&1 && git checkout master > /dev/null 2>&1")
283
+ end
284
+ set_initial_working_branch('master')
285
+ end
286
+ working_dir
287
+ end
288
+
289
+ def invalidate_distro_map
290
+ invalid_map = File.join(gem_root,'features','support','_invalid_distro_map.yml')
291
+ FileUtils.cp(invalid_map,File.join(docs_root,'_distro_map.yml'))
292
+ system("cd #{working_dir} && git add . > /dev/null && git commit -am 'Commit invalid distro map' > /dev/null")
293
+ end
294
+
295
+ def initialize_remote_repo
296
+ remote_dir = Dir.mktmpdir('ascii_binder-cucumber-remote')
297
+ FileUtils.rm_rf(remote_dir)
298
+ track_tmp_dir(remote_dir)
299
+ if run_command('create',[],remote_dir)[:status].exitstatus == 0
300
+ clone_map = File.join(gem_root,'features','support','_clone_distro_map.yml')
301
+ FileUtils.cp(clone_map,File.join(remote_dir,'_distro_map.yml'))
302
+ system("cd #{remote_dir} && git add . > /dev/null && git commit -am 'remote commit' > /dev/null && git checkout -b branch1 > /dev/null 2>&1 && git checkout master > /dev/null 2>&1")
303
+ else
304
+ puts "ERROR: Could not initialize remote repo"
305
+ exit 1
306
+ end
307
+ remote_dir
308
+ end
309
+
310
+ def status_check(step_output,error_message)
311
+ unless step_output[:status].exitstatus == 0
312
+ puts "ERROR: #{error_message}"
313
+ print_output(step_output)
314
+ exit 1
315
+ end
316
+ end
317
+
318
+ def build_check(scope,target='')
319
+ # Initial state of check_map matches ':default' scope
320
+ check_map = {
321
+ :current_branch_only => true,
322
+ :specified_distro_only => false,
323
+ :specified_page_only => false,
324
+ }
325
+ case scope
326
+ when :default
327
+ # Change nothing
328
+ when :distro
329
+ check_map[:specified_distro_only] = true
330
+ when :all_branches
331
+ check_map[:current_branch_only] = false
332
+ when :page
333
+ check_map[:specified_page_only] = true
334
+ else
335
+ puts "ERROR: Build scope '#{scope}' not recognized."
336
+ exit 1
337
+ end
338
+
339
+ # Make sure the build finished on the same branch where it started.
340
+ git = Git.open(working_dir)
341
+ current_working_branch = git.branch.name
342
+ unless current_working_branch == initial_working_branch
343
+ puts "ERROR: Build operation started on branch '#{initial_working_branch}' but ended on branch '#{current_working_branch}'"
344
+ exit 1
345
+ end
346
+
347
+ # generate the expected preview paths for each full distro + branch combo
348
+ all_paths_map = distro_preview_path_map
349
+
350
+ # get all of the paths in the actual preview directory
351
+ real_preview_info = actual_preview_info
352
+
353
+ gen_paths_map = real_preview_info[:map]
354
+ branch_count = real_preview_info[:branches].length
355
+ distro_count = real_preview_info[:distros].length
356
+ page_count = real_preview_info[:pages].length
357
+ target_distro = real_preview_info[:distros][0]
358
+ target_page = real_preview_info[:pages][0].split('/').join(':').split('.')[0]
359
+
360
+
361
+ if distro_count == 0 or branch_count == 0
362
+ puts "ERROR: A build operation should produce at least one distro / branch preview."
363
+ exit 1
364
+ end
365
+
366
+ # Compare branches by count
367
+ if branch_count > 1 and check_map[:current_branch_only]
368
+ puts "ERROR: Expected behavior for '#{scope}' scope is to build current working branch only."
369
+ exit 1
370
+ elsif branch_count == 1 and not check_map[:current_branch_only]
371
+ puts "ERROR: Expected behavior for '#{scope}' scope is to build all local branches."
372
+ exit 1
373
+ end
374
+
375
+ # Compare distros by count
376
+ if distro_count > 1 and check_map[:specified_distro_only]
377
+ puts "ERROR: Expected behavior for '#{scope}' scope is to build specified branch ('#{target}') only."
378
+ exit 1
379
+ elsif distro_count == 1
380
+ if not check_map[:specified_distro_only]
381
+ puts "ERROR: Expected behavior for '#{scope}' scope is to build all distros."
382
+ exit 1
383
+ elsif not target_distro == target
384
+ puts "ERROR: The build did not run for the expected target distro '#{target}' but instead for '#{target_distro}'"
385
+ exit 1
386
+ end
387
+ end
388
+
389
+ # Compare pages by count
390
+ if page_count > 1 and check_map[:specified_page_only]
391
+ puts "ERROR: Expected behavior for '#{scope}' is to build the specified page ('#{target}') only."
392
+ exit 1
393
+ elsif page_count == 1
394
+ if not check_map[:specified_page_only]
395
+ puts "ERROR: Expected behavior for '#{scope}' scope is to build all pages."
396
+ exit 1
397
+ elsif not target_page == target
398
+ puts "ERROR: The build did not run for the expected target page '#{target}' but instead for '#{target_page}'"
399
+ end
400
+ end
401
+
402
+ # Generated files vs expected files.
403
+ if not check_map[:specified_page_only]
404
+ all_paths_map.keys.each do |distro|
405
+ next if check_map[:specified_distro_only] and not distro == target
406
+ if not gen_paths_map.has_key?(distro)
407
+ puts "ERROR: Expected distro '#{distro}' was not generated for preview."
408
+ exit 1
409
+ end
410
+ all_paths_map[distro].keys.each do |branch|
411
+ next if check_map[:current_branch_only] and not branch == current_working_branch
412
+ if not gen_paths_map[distro].has_key?(branch)
413
+ puts "ERROR: Expected distro / branch combo '#{distro}' / '#{branch}' was not generated for preview."
414
+ exit 1
415
+ end
416
+ if not gen_paths_map[distro][branch] == all_paths_map[distro][branch]
417
+ puts "ERROR: Mismatch between expected and actual preview file paths for distro / branch combo '#{distro}' / '#{branch}'."
418
+ exit 1
419
+ end
420
+ end
421
+ end
422
+ end
423
+ end
424
+
425
+ def package_check(target_site='')
426
+ all_paths_map = distro_site_path_map
427
+ real_site_map = actual_site_map
428
+
429
+ real_site_map.keys.each do |site|
430
+ real_site_map[site].keys.each do |distro|
431
+ real_site_map[site][distro].keys.each do |branch|
432
+ # If a target site was specified and any content was generated for a different site, raise an error.
433
+ if not target_site == '' and not site == target_site and real_site_map[site][distro][branch].length > 0
434
+ puts "ERROR: Content was generated for site '#{site}' even though it was only expected for site '#{target_site}'"
435
+ exit 1
436
+ end
437
+
438
+ # Confirm that what was generated matches what was expected.
439
+ if (target_site == '' or site == target_site) and not real_site_map[site][distro][branch] == all_paths_map[site][distro][branch]
440
+ puts "ERROR: Mismatch between expected and actual site file paths for site / distro / branch combo '#{site}' / '#{distro}' / '#{branch}'."
441
+ exit 1
442
+ end
443
+ end
444
+ end
445
+
446
+ # Skip the next check for sites that aren't being packaged.
447
+ next unless target_site == '' or site == target_site
448
+
449
+ # Finally, confirm that the expected site index page was copied to the site home directory.
450
+ source_page = File.join(docs_root,"index-#{site}.html")
451
+ target_page = File.join(package_dir,site,'index.html')
452
+ unless FileUtils.compare_file(source_page,target_page)
453
+ puts "ERROR: Incorrect site index file contents at '#{target_page}'; expected contents of '#{source_page}'."
454
+ exit 1
455
+ end
456
+ end
457
+ end
458
+ end
459
+
460
+ World(Helpers)
461
+
462
+ Before do
463
+ working_dir
464
+ end
465
+
466
+ After do
467
+ clean_tracked_dirs
468
+ end