ascii_binder 0.1.9 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
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