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,67 @@
1
+ require 'ascii_binder/distro'
2
+ require 'trollop'
3
+ require 'yaml'
4
+
5
+ module AsciiBinder
6
+ class DistroMap
7
+ def initialize(distro_map_filepath)
8
+ @distro_yaml = YAML.load_file(distro_map_filepath)
9
+ @distro_map = {}
10
+ @distro_yaml.each do |distro_key,distro_config|
11
+ if @distro_map.has_key?(distro_key)
12
+ Trollop::die "Error parsing '#{distro_map_filepath}': distro key '#{distro_key}' is used more than once."
13
+ end
14
+ distro = AsciiBinder::Distro.new(distro_map_filepath,distro_key,distro_config)
15
+ @distro_map[distro_key] = distro
16
+ end
17
+ end
18
+
19
+ def get_distro(distro_key)
20
+ unless @distro_map.has_key?(distro_key)
21
+ Trollop::die "Distro key '#{distro_key}' does not exist"
22
+ end
23
+ @distro_map[distro_key]
24
+ end
25
+
26
+ def include_distro_key?(distro_key)
27
+ @distro_map.has_key?(distro_key)
28
+ end
29
+
30
+ def distro_keys
31
+ @distro_map.keys
32
+ end
33
+
34
+ def distros
35
+ @distro_map.values
36
+ end
37
+
38
+ def distro_branches(distro_key='')
39
+ if distro_key == ''
40
+ branch_list = []
41
+ distros.each do |distro|
42
+ branch_list.concat(distro.branch_ids)
43
+ end
44
+ return branch_list.uniq
45
+ else
46
+ return get_distro(distro_key).branch_ids
47
+ end
48
+ end
49
+
50
+ def is_valid?
51
+ @distro_map.values.each do |distro|
52
+ next if distro.is_valid?
53
+ return false
54
+ end
55
+ return true
56
+ end
57
+
58
+ def errors
59
+ errors = []
60
+ @distro_map.values.each do |distro|
61
+ next if distro.is_valid?
62
+ errors << distro.errors
63
+ end
64
+ return errors
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,581 @@
1
+ require 'ascii_binder/distro_branch'
2
+ require 'ascii_binder/distro_map'
3
+ require 'ascii_binder/helpers'
4
+ require 'ascii_binder/site_map'
5
+ require 'ascii_binder/template_renderer'
6
+ require 'ascii_binder/topic_map'
7
+ require 'asciidoctor'
8
+ require 'asciidoctor/cli'
9
+ require 'asciidoctor-diagram'
10
+ require 'fileutils'
11
+ require 'find'
12
+ require 'git'
13
+ require 'logger'
14
+ require 'pandoc-ruby'
15
+ require 'pathname'
16
+ require 'sitemap_generator'
17
+ require 'trollop'
18
+ require 'yaml'
19
+
20
+ include AsciiBinder::Helpers
21
+
22
+ module AsciiBinder
23
+ module Engine
24
+
25
+ def build_date
26
+ Time.now.utc
27
+ end
28
+
29
+ def notice(hey,message,newline = false)
30
+ # TODO: (maybe) redirect everything to stderr
31
+ if newline
32
+ puts "\n"
33
+ end
34
+ puts "#{hey}: #{message}"
35
+ end
36
+
37
+ def warning(message,newline = false)
38
+ notice("WARNING",message,newline)
39
+ end
40
+
41
+ def nl_warning(message)
42
+ warning(message,true)
43
+ end
44
+
45
+ def git
46
+ @git ||= Git.open(git_root_dir)
47
+ end
48
+
49
+ def git_checkout branch_name
50
+ target_branch = git.branches.local.select{ |b| b.name == branch_name }[0]
51
+ if not target_branch.nil? and not target_branch.current
52
+ target_branch.checkout
53
+ end
54
+ end
55
+
56
+ def git_stash_all
57
+ # See if there are any changes in need of stashing
58
+ @stash_needed = `cd #{git_root_dir} && git status --porcelain` !~ /^\s*$/
59
+ if @stash_needed
60
+ puts "\nNOTICE: Stashing uncommited changes and files in working branch."
61
+ `cd #{docs_root_dir} && git stash -u`
62
+ end
63
+ end
64
+
65
+ def git_apply_and_drop
66
+ return unless @stash_needed
67
+ puts "\nNOTE: Re-applying uncommitted changes and files to working branch."
68
+ if system("cd #{docs_root_dir} && git stash pop")
69
+ puts "NOTE: Stash application successful."
70
+ else
71
+ puts "ERROR: Could not apply stashed code. Run `git stash apply` manually."
72
+ end
73
+ @stash_needed = false
74
+ end
75
+
76
+ # Returns the local git branches; current branch is always first
77
+ def local_branches
78
+ @local_branches ||= begin
79
+ branches = []
80
+ if not git.branches.local.empty?
81
+ branches << git.branches.local.select{ |b| b.current }[0].name
82
+ branches << git.branches.local.select{ |b| not b.current }.map{ |b| b.name }
83
+ end
84
+ branches.flatten
85
+ end
86
+ end
87
+
88
+ def working_branch
89
+ @working_branch ||= local_branches[0]
90
+ end
91
+
92
+ def dir_empty?(dir)
93
+ Dir.entries(dir).select{ |f| not f.start_with?('.') }.empty?
94
+ end
95
+
96
+
97
+ # Protip: Don't cache these! The topic map needs to be reread every time we change branches.
98
+ def topic_map_file
99
+ topic_file = TOPIC_MAP_FILENAME
100
+ unless File.exist?(File.join(docs_root_dir,topic_file))
101
+ # The new filename '_topic_map.yml' couldn't be found;
102
+ # switch to the old one and warn the user.
103
+ topic_file = BUILD_FILENAME
104
+ unless File.exist?(File.join(docs_root_dir,topic_file))
105
+ # Critical error - no topic map file at all.
106
+ Trollop::die "Could not find any topic map file ('#{TOPIC_MAP_FILENAME}' or '#{BUILD_FILENAME}') at #{docs_root_dir} in branch '#{git.branch}'"
107
+ end
108
+ warning "'#{BUILD_FILENAME}' is a deprecated filename. Rename this to '#{TOPIC_MAP_FILENAME}'."
109
+ end
110
+ topic_file
111
+ end
112
+
113
+ def topic_map
114
+ topic_map = AsciiBinder::TopicMap.new(topic_map_file,distro_map.distro_keys)
115
+ unless topic_map.is_valid?
116
+ errors = topic_map.errors
117
+ Trollop::die "The topic map file at '#{topic_map_file}' contains the following errors:\n- " + errors.join("\n- ") + "\n"
118
+ end
119
+ return topic_map
120
+ end
121
+
122
+ def create_new_repo
123
+ gem_template_dir = File.join(gem_root_dir,"templates")
124
+
125
+ # Create the new repo dir
126
+ FileUtils.mkdir_p(docs_root_dir)
127
+
128
+ # Copy the basic repo content into the new repo dir
129
+ Find.find(gem_template_dir).each do |path|
130
+ next if path == gem_template_dir
131
+ src_path = Pathname.new(path)
132
+ tgt_path = src_path.sub(gem_template_dir,docs_root_dir)
133
+ if src_path.directory?
134
+ FileUtils.mkdir_p(tgt_path.to_s)
135
+ else
136
+ FileUtils.cp src_path.to_s, tgt_path.to_s
137
+ end
138
+ end
139
+
140
+ # Initialize the git repo
141
+ Git.init(docs_root_dir)
142
+ end
143
+
144
+ def find_topic_files
145
+ file_list = []
146
+ Find.find(docs_root_dir).each do |path|
147
+ # Only consider .adoc files and ignore README, and anything in
148
+ # directories whose names begin with 'old' or '_' (underscore)
149
+ next if path.nil? or not path =~ /.*\.adoc/ or path =~ /README/ or path =~ /\/old\// or path =~ /\/_/
150
+ src_path = Pathname.new(path).sub(docs_root_dir,'').to_s
151
+ next if src_path.split('/').length < 3
152
+ file_list << src_path
153
+ end
154
+ file_list.map{ |path|
155
+ parts = path.split('/').slice(1..-1);
156
+ parts.slice(0..-2).join('/') + '/' + parts[-1].split('.')[0]
157
+ }
158
+ end
159
+
160
+ def remove_found_topic_files(branch,branch_topic_map,branch_topic_files)
161
+ nonexistent_topics = []
162
+ branch_topic_map.filepaths.each do |topic_map_filepath|
163
+ result = branch_topic_files.delete(topic_map_filepath)
164
+ if result.nil?
165
+ nonexistent_topics << topic_map_filepath
166
+ end
167
+ end
168
+ if nonexistent_topics.length > 0
169
+ nl_warning "The #{topic_map_file} file on branch '#{branch}' references nonexistent topics:\n" + nonexistent_topics.map{ |topic| "- #{topic}" }.join("\n")
170
+ end
171
+ end
172
+
173
+ def distro_map
174
+ @distro_map ||= begin
175
+ distro_map_file = File.join(docs_root_dir, DISTRO_MAP_FILENAME)
176
+ distro_map = AsciiBinder::DistroMap.new(distro_map_file)
177
+ unless distro_map.is_valid?
178
+ errors = distro_map.errors
179
+ Trollop::die "The distro map file at '#{distro_map_file}' contains the following errors:\n- " + errors.join("\n- ") + "\n"
180
+ end
181
+ distro_map
182
+ end
183
+ end
184
+
185
+ def site_map
186
+ @site_map ||= AsciiBinder::SiteMap.new(distro_map)
187
+ end
188
+
189
+ def branch_group_branches
190
+ @branch_group_branches ||= begin
191
+ group_branches = Hash.new
192
+ group_branches[:working_only] = [local_branches[0]]
193
+ group_branches[:publish] = distro_map.distro_branches
194
+ site_map.sites.each do |site|
195
+ group_branches["publish_#{site.id}".to_sym] = site.branches
196
+ end
197
+ group_branches[:all] = local_branches
198
+ group_branches
199
+ end
200
+ end
201
+
202
+ def page(args)
203
+ # TODO: This process of rebuilding the entire nav for every page will not scale well.
204
+ # As the doc set increases, we will need to think about refactoring this.
205
+ args[:breadcrumb_root], args[:breadcrumb_group], args[:breadcrumb_subgroup], args[:breadcrumb_topic] = extract_breadcrumbs(args)
206
+
207
+ args[:breadcrumb_subgroup_block] = ''
208
+ args[:subtopic_shim] = ''
209
+ if args[:breadcrumb_subgroup]
210
+ args[:breadcrumb_subgroup_block] = "<li class=\"hidden-xs active\">#{args[:breadcrumb_subgroup]}</li>"
211
+ args[:subtopic_shim] = '../'
212
+ end
213
+
214
+ template_path = File.expand_path("#{docs_root_dir}/_templates/page.html.erb")
215
+ template_renderer.render(template_path, args)
216
+ end
217
+
218
+ def extract_breadcrumbs(args)
219
+ breadcrumb_root = breadcrumb_group = breadcrumb_subgroup = breadcrumb_topic = nil
220
+
221
+ root_group = args[:navigation].first
222
+ selected_group = args[:navigation].detect { |group| group[:id] == args[:group_id] }
223
+ selected_subgroup = selected_group[:topics].detect { |subgroup| subgroup[:id] == args[:subgroup_id] }
224
+ current_is_subtopic = selected_subgroup ? true : false
225
+
226
+ if root_group
227
+ root_topic = root_group[:topics].first
228
+ breadcrumb_root = linkify_breadcrumb(root_topic[:path], "#{args[:distro]} #{args[:version]}", current_is_subtopic) if root_topic
229
+ end
230
+
231
+ if selected_group
232
+ group_topic = selected_group[:topics].first
233
+ breadcrumb_group = linkify_breadcrumb(group_topic[:path], selected_group[:name], current_is_subtopic) if group_topic
234
+
235
+ if selected_subgroup
236
+ subgroup_topic = selected_subgroup[:topics].first
237
+ breadcrumb_subgroup = linkify_breadcrumb(subgroup_topic[:path], selected_subgroup[:name], current_is_subtopic) if subgroup_topic
238
+
239
+ selected_topic = selected_subgroup[:topics].detect { |topic| topic[:id] == args[:topic_id] }
240
+ breadcrumb_topic = linkify_breadcrumb(nil, selected_topic[:name], current_is_subtopic) if selected_topic
241
+ else
242
+ selected_topic = selected_group[:topics].detect { |topic| topic[:id] == args[:topic_id] }
243
+ breadcrumb_topic = linkify_breadcrumb(nil, selected_topic[:name], current_is_subtopic) if selected_topic
244
+ end
245
+ end
246
+
247
+ return breadcrumb_root, breadcrumb_group, breadcrumb_subgroup, breadcrumb_topic
248
+ end
249
+
250
+ def linkify_breadcrumb(href, text, extra_level)
251
+ addl_level = extra_level ? '../' : ''
252
+ href ? "<a href=\"#{addl_level}#{href}\">#{text}</a>" : text
253
+ end
254
+
255
+ def asciidoctor_page_attrs(more_attrs=[])
256
+ [
257
+ 'source-highlighter=coderay',
258
+ 'coderay-css=style',
259
+ 'linkcss!',
260
+ 'icons=font',
261
+ 'idprefix=',
262
+ 'idseparator=-',
263
+ 'sectanchors',
264
+ 'data-uri',
265
+ ].concat(more_attrs)
266
+ end
267
+
268
+ def generate_docs(branch_group,build_distro,single_page)
269
+ # First, test to see if the docs repo has any commits. If the user has just
270
+ # run `asciibinder create`, there will be no commits to work from, yet.
271
+ if local_branches.empty?
272
+ raise "Before you can build the docs, you need at least one commit in your docs repo."
273
+ end
274
+
275
+ # Make a filepath in list form from the single_page argument
276
+ single_page_path = []
277
+ if not single_page.nil?
278
+ single_page_path = single_page.split(':')[0].split('/')
279
+ single_page_path << single_page.split(':')[1]
280
+ puts "Rebuilding '#{single_page_path.join('/')}' on branch '#{working_branch}'."
281
+ end
282
+
283
+ if not build_distro == ''
284
+ if not distro_map.include_distro_key?(build_distro)
285
+ exit
286
+ else
287
+ puts "Building only the #{distro_map.get_distro(build_distro).name} distribution."
288
+ end
289
+ elsif single_page.nil?
290
+ puts "Building all distributions."
291
+ end
292
+
293
+ # Notify the user of missing local branches
294
+ missing_branches = []
295
+ distro_map.distro_branches(build_distro).sort.each do |dbranch|
296
+ next if local_branches.include?(dbranch)
297
+ missing_branches << dbranch
298
+ end
299
+ if missing_branches.length > 0 and single_page.nil?
300
+ puts "\nNOTE: The following branches do not exist in your local git repo:"
301
+ missing_branches.each do |mbranch|
302
+ puts "- #{mbranch}"
303
+ end
304
+ puts "The build will proceed but these branches will not be generated."
305
+ end
306
+
307
+ # Generate all distros for all branches in the indicated branch group
308
+ branch_group_branches[branch_group].each do |local_branch|
309
+ # Skip known missing branches; this will only come up for the :publish branch group
310
+ next if missing_branches.include?(local_branch)
311
+
312
+ # Single-page regen only occurs for the working branch
313
+ if not local_branch == working_branch
314
+ if single_page.nil?
315
+ # Checkout the branch
316
+ puts "\nCHANGING TO BRANCH '#{local_branch}'"
317
+ git_checkout(local_branch)
318
+ else
319
+ next
320
+ end
321
+ end
322
+
323
+ # Note the image files checked in to this branch.
324
+ branch_image_files = Find.find(docs_root_dir).select{ |path| not path.nil? and (path =~ /.*\.png$/ or path =~ /.*\.png\.cache$/) }
325
+
326
+ first_branch = single_page.nil?
327
+
328
+ if local_branch =~ /^\(detached from .*\)/
329
+ local_branch = 'detached'
330
+ end
331
+
332
+ # The branch_orphan_files list starts with the set of all
333
+ # .adoc files found in the repo, and will be whittled
334
+ # down from there.
335
+ branch_orphan_files = find_topic_files
336
+ branch_topic_map = topic_map
337
+ remove_found_topic_files(local_branch,branch_topic_map,branch_orphan_files)
338
+
339
+ if branch_orphan_files.length > 0 and single_page.nil?
340
+ nl_warning "Branch '#{local_branch}' includes the following .adoc files that are not referenced in the #{topic_map_file} file:\n" + branch_orphan_files.map{ |file| "- #{file}" }.join("\n")
341
+ end
342
+
343
+ # Run all distros.
344
+ distro_map.distros.each do |distro|
345
+ if not build_distro == ''
346
+ # Only building a single distro; build for all indicated branches, skip the others.
347
+ next unless build_distro == distro.id
348
+ else
349
+ current_distro_branches = distro_map.distro_branches(distro.id)
350
+
351
+ # In publish mode we only build "valid" distro-branch combos from the distro map
352
+ if branch_group.to_s.start_with?("publish") and not current_distro_branches.include?(local_branch)
353
+ next
354
+ end
355
+
356
+ # In "build all" mode we build every distro on the working branch plus the publish distro-branch combos
357
+ if branch_group == :all and not local_branch == working_branch and not current_distro_branches.include?(local_branch)
358
+ next
359
+ end
360
+ end
361
+
362
+ # Get the current distro / branch object
363
+ branch_config = AsciiBinder::DistroBranch.new('',{ "name" => "Branch Build", "dir" => local_branch },distro)
364
+ dev_branch = true
365
+ if distro.branch_ids.include?(local_branch)
366
+ branch_config = distro.branch(local_branch)
367
+ dev_branch = false
368
+ end
369
+
370
+ if first_branch
371
+ puts "\nBuilding #{distro.name} for branch '#{local_branch}'"
372
+ first_branch = false
373
+ end
374
+
375
+ # Copy files into the preview area.
376
+ [[stylesheet_dir, '*css', branch_config.branch_stylesheet_dir],
377
+ [javascript_dir, '*js', branch_config.branch_javascript_dir],
378
+ [image_dir, '*', branch_config.branch_image_dir]].each do |dgroup|
379
+ src_dir = dgroup[0]
380
+ glob = dgroup[1]
381
+ tgt_dir = dgroup[2]
382
+ if Dir.exist?(src_dir) and not dir_empty?(src_dir)
383
+ FileUtils.mkdir_p tgt_dir
384
+ FileUtils.cp_r Dir.glob(File.join(src_dir,glob)), tgt_dir
385
+ end
386
+ end
387
+
388
+ # Build the navigation structure for this branch / distro
389
+ navigation = branch_topic_map.nav_tree(distro.id)
390
+
391
+ # Build the topic files for this branch & distro
392
+ process_topic_entity_list(branch_config,single_page_path,navigation,branch_topic_map.list)
393
+ end
394
+
395
+ # In single-page context, we're done.
396
+ if not single_page.nil?
397
+ #exit 200
398
+ return
399
+ end
400
+
401
+ # Remove DITAA-generated images
402
+ ditaa_image_files = Find.find(docs_root_dir).select{ |path| not path.nil? and not (path =~ /_preview/ or path =~ /_package/) and (path =~ /.*\.png$/ or path =~ /.*\.png\.cache$/) and not branch_image_files.include?(path) }
403
+ if not ditaa_image_files.empty?
404
+ puts "\nRemoving ditaa-generated files from repo before changing branches."
405
+ ditaa_image_files.each do |dfile|
406
+ File.unlink(dfile)
407
+ end
408
+ end
409
+
410
+ if local_branch == working_branch
411
+ # We're moving away from the working branch, so save off changed files
412
+ git_stash_all
413
+ end
414
+ end
415
+
416
+ # Return to the original branch
417
+ git_checkout(working_branch)
418
+
419
+ # If necessary, restore temporarily stashed files
420
+ git_apply_and_drop
421
+
422
+ puts "\nAll builds completed."
423
+ end
424
+
425
+ def process_topic_entity_list(branch_config,single_page_path,navigation,topic_entity_list,preview_path='')
426
+ # When called from a topic group entity, create the preview dir for that group
427
+ Dir.mkdir(preview_path) unless preview_path == '' or File.exists?(preview_path)
428
+
429
+ topic_entity_list.each do |topic_entity|
430
+ # If this topic entity or any potential subentities are outside of the distro or single-page params, skip it.
431
+ next unless topic_entity.include?(branch_config.distro.id,single_page_path)
432
+
433
+ if topic_entity.is_group?
434
+ preview_path = topic_entity.preview_path(branch_config.distro.id,branch_config.dir)
435
+ process_topic_entity_list(branch_config,single_page_path,navigation,topic_entity.subitems,preview_path)
436
+ elsif topic_entity.is_topic?
437
+ if single_page_path.length == 0
438
+ puts " - #{topic_entity.repo_path}"
439
+ end
440
+ configure_and_generate_page(topic_entity,branch_config,navigation)
441
+ end
442
+ end
443
+ end
444
+
445
+ def configure_and_generate_page(topic,branch_config,navigation)
446
+ distro = branch_config.distro
447
+ topic_adoc = File.open(topic.source_path,'r').read
448
+
449
+ page_attrs = asciidoctor_page_attrs([
450
+ "imagesdir=#{File.join(topic.parent.source_path,'images')}",
451
+ branch_config.distro.id,
452
+ "product-title=#{branch_config.distro_name}",
453
+ "product-version=#{branch_config.name}",
454
+ "product-author=#{branch_config.distro_author}"
455
+ ])
456
+
457
+ doc = Asciidoctor.load topic_adoc, :header_footer => false, :safe => :unsafe, :attributes => page_attrs
458
+ article_title = doc.doctitle || topic.name
459
+
460
+ topic_html = doc.render
461
+ dir_depth = ''
462
+ if branch_config.dir.split('/').length > 1
463
+ dir_depth = '../' * (branch_config.dir.split('/').length - 1)
464
+ end
465
+
466
+ # This is logic bridges newer arbitrary-depth-tolerant code to
467
+ # older depth-limited code. Truly removing depth limitations will
468
+ # require changes to page templates in user docs repos.
469
+ breadcrumb = topic.breadcrumb
470
+ group_title = breadcrumb[0][:name]
471
+ group_id = breadcrumb[0][:id]
472
+ topic_title = breadcrumb[-1][:name]
473
+ topic_id = breadcrumb[-1][:id]
474
+ subgroup_title = nil
475
+ subgroup_id = nil
476
+ if breadcrumb.length == 3
477
+ subgroup_title = breadcrumb[1][:name]
478
+ subgroup_id = breadcrumb[1][:id]
479
+ dir_depth = '../' + dir_depth
480
+ end
481
+
482
+ preview_path = topic.preview_path(distro.id,branch_config.dir)
483
+
484
+ page_args = {
485
+ :distro_key => distro.id,
486
+ :distro => branch_config.distro_name,
487
+ :site_name => distro.site.name,
488
+ :site_url => distro.site.url,
489
+ :topic_url => preview_path,
490
+ :version => branch_config.name,
491
+ :group_title => group_title,
492
+ :subgroup_title => subgroup_title,
493
+ :topic_title => topic_title,
494
+ :article_title => article_title,
495
+ :content => topic_html,
496
+ :navigation => navigation,
497
+ :group_id => group_id,
498
+ :subgroup_id => subgroup_id,
499
+ :topic_id => topic_id,
500
+ :css_path => "../../#{dir_depth}#{branch_config.dir}/#{STYLESHEET_DIRNAME}/",
501
+ :javascripts_path => "../../#{dir_depth}#{branch_config.dir}/#{JAVASCRIPT_DIRNAME}/",
502
+ :images_path => "../../#{dir_depth}#{branch_config.dir}/#{IMAGE_DIRNAME}/",
503
+ :site_home_path => "../../#{dir_depth}index.html",
504
+ :template_path => template_dir,
505
+ }
506
+ full_file_text = page(page_args)
507
+ File.write(preview_path,full_file_text)
508
+ end
509
+
510
+ # package_docs
511
+ # This method generates the docs and then organizes them the way they will be arranged
512
+ # for the production websites.
513
+ def package_docs(package_site)
514
+ site_map.sites.each do |site|
515
+ next if not package_site == '' and not package_site == site.id
516
+ site.distros.each do |distro_id,branches|
517
+ branches.each do |branch|
518
+ src_dir = File.join(preview_dir,distro_id,branch.dir)
519
+ tgt_tdir = branch.dir.split('/')
520
+ tgt_tdir.pop
521
+ tgt_dir = ''
522
+ if tgt_tdir.length > 0
523
+ tgt_dir = File.join(package_dir,site.id,tgt_tdir.join('/'))
524
+ else
525
+ tgt_dir = File.join(package_dir,site.id)
526
+ end
527
+ next if not File.directory?(src_dir)
528
+ FileUtils.mkdir_p(tgt_dir)
529
+ FileUtils.cp_r(src_dir,tgt_dir)
530
+ end
531
+ site_dir = File.join(package_dir,site.id)
532
+ if File.directory?(site_dir)
533
+ puts "\nPackaging #{distro_id} for #{site.id} site."
534
+
535
+ # Any files in the root of the docs repo with names ending in:
536
+ # *-#{site}.html
537
+ # will get copied into the root dir of the packaged site with
538
+ # the site name stripped out.
539
+ #
540
+ # Example: for site name 'commercial', the files:
541
+ # * index-commercial.html would end up as #{site_root}/index.html
542
+ # * search-commercial.html would end up as #{site_root}/search.html
543
+ # * index-community.html would be ignored
544
+ site_files = Dir.glob(File.join(docs_root_dir, '*-' + site.id + '.html'))
545
+ unless site_files.empty?
546
+ site_files.each do |fpath|
547
+ target_basename = File.basename(fpath).gsub(/-#{site.id}\.html$/, '.html')
548
+ FileUtils.cp(fpath,File.join(package_dir,site.id,target_basename))
549
+ end
550
+ else
551
+ FileUtils.cp(File.join(preview_dir,distro_id,'index.html'),File.join(package_dir,site.id,'index.html'))
552
+ end
553
+ ['_images','_stylesheets'].each do |support_dir|
554
+ FileUtils.cp_r(File.join(docs_root_dir,support_dir),File.join(package_dir,site.id,support_dir))
555
+ end
556
+
557
+ # Now build a sitemap
558
+ site_dir_path = Pathname.new(site_dir)
559
+ SitemapGenerator::Sitemap.create(
560
+ :default_host => site.url,
561
+ :public_path => site_dir_path,
562
+ :compress => false,
563
+ :filename => File.join(site_dir,'sitemap')
564
+ ) do
565
+ file_list = Find.find(site_dir).select{ |path| not path.nil? and path =~ /.*\.html$/ }.map{ |path| '/' + Pathname.new(path).relative_path_from(site_dir_path).to_s }
566
+ file_list.each do |file|
567
+ add(file, :changefreq => 'daily')
568
+ end
569
+ end
570
+ end
571
+ end
572
+ end
573
+ end
574
+
575
+ def clean_up
576
+ if not system("rm -rf #{docs_root_dir}/_preview/* #{docs_root_dir}/_package/*")
577
+ puts "Nothing to clean."
578
+ end
579
+ end
580
+ end
581
+ end