bookbindery 3.0.1 → 3.1.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bookbinder.rb +0 -4
  3. data/lib/bookbinder/archive.rb +12 -5
  4. data/lib/bookbinder/archive_menu_configuration.rb +4 -4
  5. data/lib/bookbinder/commands/bind.rb +29 -97
  6. data/lib/bookbinder/commands/bind/bind_options.rb +50 -0
  7. data/lib/bookbinder/commands/bind/directory_preparer.rb +13 -12
  8. data/lib/bookbinder/commands/build_and_push_tarball.rb +1 -8
  9. data/lib/bookbinder/commands/help.rb +1 -1
  10. data/lib/bookbinder/commands/run_publish_ci.rb +1 -3
  11. data/lib/bookbinder/commands/tag.rb +12 -9
  12. data/lib/bookbinder/commands/update_local_doc_repos.rb +23 -7
  13. data/lib/bookbinder/config/bind_config_factory.rb +5 -7
  14. data/lib/bookbinder/config/remote_bind_configuration.rb +23 -31
  15. data/lib/bookbinder/config/section_config.rb +56 -0
  16. data/lib/bookbinder/configuration.rb +58 -18
  17. data/lib/bookbinder/configuration_fetcher.rb +3 -5
  18. data/lib/bookbinder/configuration_validator.rb +1 -8
  19. data/lib/bookbinder/distributor.rb +1 -1
  20. data/lib/bookbinder/dita_command_creator.rb +60 -16
  21. data/lib/bookbinder/dita_html_to_middleman_formatter.rb +3 -2
  22. data/lib/bookbinder/errors/programmer_mistake.rb +5 -0
  23. data/lib/bookbinder/git_accessor.rb +36 -2
  24. data/lib/bookbinder/ingest/cloner_factory.rb +3 -3
  25. data/lib/bookbinder/ingest/destination_directory.rb +7 -1
  26. data/lib/bookbinder/ingest/{git_hub_repository_cloner.rb → git_cloner.rb} +3 -2
  27. data/lib/bookbinder/ingest/repo_identifier.rb +45 -0
  28. data/lib/bookbinder/local_file_system_accessor.rb +4 -9
  29. data/lib/bookbinder/middleman_runner.rb +5 -6
  30. data/lib/bookbinder/preprocessing/copy_to_site_gen_dir.rb +27 -0
  31. data/lib/bookbinder/preprocessing/dita_preprocessor.rb +103 -0
  32. data/lib/bookbinder/preprocessing/preprocessor.rb +26 -0
  33. data/lib/bookbinder/repositories/command_repository.rb +17 -21
  34. data/lib/bookbinder/repositories/section_repository.rb +24 -16
  35. data/lib/bookbinder/repositories/section_repository_factory.rb +19 -0
  36. data/lib/bookbinder/streams/{switchable_stdout_and_red_stderr.rb → colorized_stream.rb} +0 -17
  37. data/lib/bookbinder/time_fetcher.rb +7 -0
  38. data/lib/bookbinder/validation_checkers/dita_section_checker.rb +2 -2
  39. data/lib/bookbinder/values/output_locations.rb +13 -12
  40. data/lib/bookbinder/values/section.rb +22 -5
  41. data/master_middleman/bookbinder_helpers.rb +4 -11
  42. metadata +59 -75
  43. data/lib/bookbinder/book.rb +0 -59
  44. data/lib/bookbinder/config/local_bind_configuration.rb +0 -23
  45. data/lib/bookbinder/dita_preprocessor.rb +0 -68
  46. data/lib/bookbinder/dita_section_gatherer_factory.rb +0 -23
  47. data/lib/bookbinder/git_client.rb +0 -66
  48. data/lib/bookbinder/git_hub_repository.rb +0 -101
  49. data/lib/bookbinder/local_dita_section_gatherer.rb +0 -32
  50. data/lib/bookbinder/remote_dita_section_gatherer.rb +0 -35
  51. data/lib/bookbinder/validation_checkers/config_version_checker.rb +0 -91
  52. data/lib/bookbinder/values/code_example.rb +0 -7
  53. data/lib/bookbinder/values/dita_section.rb +0 -39
@@ -1,12 +1,12 @@
1
- require_relative '../bookbinder/values/dita_section'
2
-
3
1
  module Bookbinder
4
2
  class DitaCommandCreator
3
+ MissingDitaOTFlagValue = Class.new(RuntimeError)
4
+
5
5
  def initialize(path_to_dita_ot_library)
6
6
  @path_to_dita_ot_library = path_to_dita_ot_library
7
7
  end
8
8
 
9
- def convert_to_html_command(dita_section, write_to: nil)
9
+ def convert_to_html_command(dita_section, dita_flags: nil, write_to: nil)
10
10
  classpath = "#{path_to_dita_ot_library}/lib/xercesImpl.jar:" +
11
11
  "#{path_to_dita_ot_library}/lib/xml-apis.jar:" +
12
12
  "#{path_to_dita_ot_library}/lib/resolver.jar:" +
@@ -17,23 +17,67 @@ module Bookbinder
17
17
  "#{path_to_dita_ot_library}:" +
18
18
  "#{path_to_dita_ot_library}/lib/:" +
19
19
  "#{path_to_dita_ot_library}/lib/dost.jar"
20
- command = "export CLASSPATH=#{classpath}; " +
21
- "ant -f #{path_to_dita_ot_library} " +
22
- "-Dbasedir='/' " +
23
- "-Doutput.dir=#{write_to} " +
24
- "-Dtranstype='tocjs' " +
25
- "-Ddita.temp.dir='/tmp/bookbinder_dita' " +
26
- "-Dgenerate.copy.outer='2' " +
27
- "-Dargs.input=#{dita_section.absolute_path_to_ditamap} "
28
-
29
- if dita_section.absolute_path_to_ditaval
30
- command += "-Dargs.filter=#{dita_section.absolute_path_to_ditaval} "
31
- end
32
- command
20
+
21
+ "export CLASSPATH=#{classpath}; " +
22
+ "ant -f #{path_to_dita_ot_library} " +
23
+ unduplicated_flags(
24
+ write_to: write_to,
25
+ dita_flags: dita_flags,
26
+ ditamap_path: dita_section.path_to_preprocessor_attribute('ditamap_location'),
27
+ ditaval_path: dita_section.path_to_preprocessor_attribute('ditaval_location'),
28
+ )
33
29
  end
34
30
 
35
31
  private
36
32
 
33
+ def unduplicated_flags(write_to: nil, ditamap_path: nil, ditaval_path: nil, dita_flags: dita_flags)
34
+ arg_flags = {
35
+ 'output.dir' => write_to,
36
+ 'args.input' => ditamap_path,
37
+ }.merge(filter(ditaval_path))
38
+ all_flags = arg_flags.merge(base_flags.merge(optional_flags dita_flags))
39
+ format(all_flags)
40
+ end
41
+
42
+ def filter(ditaval_path)
43
+ if ditaval_path
44
+ { 'args.filter' => ditaval_path }
45
+ else
46
+ {}
47
+ end
48
+ end
49
+
50
+ def base_flags
51
+ {
52
+ 'basedir' => '/',
53
+ 'transtype' => 'tocjs',
54
+ 'dita.temp.dir' => '/tmp/bookbinder_dita',
55
+ 'generate.copy.outer' => '2',
56
+ 'outer.control' => 'warn'
57
+ }
58
+ end
59
+
60
+ def optional_flags(flags_str)
61
+ flags = flags_str ? flags_str.split(" ") : []
62
+ flags.inject({}) do |h, f|
63
+ k,v = f.split('=')
64
+ h[k] = v
65
+ raise MissingDitaOTFlagValue.new("The DITA-flag '#{k}' that you passed is missing a value. Please pass your DITA option in the format '#{k}=<value>'.") unless v
66
+ h
67
+ end
68
+ end
69
+
70
+ def format(flags)
71
+ flags.inject("") do |res, f|
72
+ k,v = f
73
+ res += "-D#{k}='#{stripped_flag_value v}' "
74
+ end
75
+ end
76
+
77
+ def stripped_flag_value(v)
78
+ v.to_s.gsub(/['|"]/, "")
79
+ end
80
+
37
81
  attr_reader :path_to_dita_ot_library
38
82
  end
39
83
  end
@@ -29,11 +29,12 @@ module Bookbinder
29
29
  end
30
30
  end
31
31
 
32
- def format_subnav(dita_section,
32
+ def format_subnav(path_to_dita_section,
33
33
  subnav_template_text,
34
34
  json_props_location,
35
35
  unformatted_subnav_text)
36
- formatted_json_links = subnav_formatter.get_links_as_json(unformatted_subnav_text, dita_section.directory)
36
+ formatted_json_links = subnav_formatter.get_links_as_json(unformatted_subnav_text,
37
+ path_to_dita_section)
37
38
 
38
39
  nav_text = html_document_manipulator.set_attribute(document: subnav_template_text,
39
40
  selector: 'div.nav-content',
@@ -0,0 +1,5 @@
1
+ module Bookbinder
2
+ module Errors
3
+ ProgrammerMistake = Class.new(RuntimeError)
4
+ end
5
+ end
@@ -2,6 +2,9 @@ require 'git'
2
2
 
3
3
  module Bookbinder
4
4
  class GitAccessor
5
+ TagExists = Class.new(RuntimeError)
6
+ InvalidTagRef = Class.new(RuntimeError)
7
+
5
8
  def initialize
6
9
  @cache = {}
7
10
  end
@@ -12,12 +15,39 @@ module Bookbinder
12
15
  end
13
16
  end
14
17
 
18
+ def update(cloned_path)
19
+ Git.open(cloned_path).pull
20
+ end
21
+
15
22
  def read_file(filename, from_repo: nil, checkout: 'master')
16
23
  Dir.mktmpdir do |dir|
17
24
  path = Pathname(dir)
18
- git = _clone(from_repo, "read-file", path)
25
+ git = _clone(from_repo, temp_name("read-file"), path)
19
26
  git.checkout(checkout)
20
- path.join("read-file", filename).read
27
+ path.join(temp_name("read-file"), filename).read
28
+ end
29
+ end
30
+
31
+ def remote_tag(url, tagname, commit_or_object)
32
+ Dir.mktmpdir do |dir|
33
+ path = Pathname(dir)
34
+ git = _clone(url, temp_name("tag"), path)
35
+ git.config('user.name', 'Bookbinder')
36
+ git.config('user.email', 'bookbinder@cloudfoundry.org')
37
+ begin
38
+ git.add_tag(tagname, "origin/#{commit_or_object}",
39
+ message: 'Tagged by Bookbinder')
40
+ rescue Git::GitExecuteError => e
41
+ case e.message
42
+ when /already exists/
43
+ raise TagExists
44
+ when /as a valid ref/
45
+ raise InvalidTagRef
46
+ else
47
+ raise
48
+ end
49
+ end
50
+ git.push("origin", "master", tags: true)
21
51
  end
22
52
  end
23
53
 
@@ -25,6 +55,10 @@ module Bookbinder
25
55
 
26
56
  attr_reader :cache
27
57
 
58
+ def temp_name(purpose)
59
+ "bookbinder-git-accessor-#{purpose}"
60
+ end
61
+
28
62
  def cached_clone(url, name, path)
29
63
  cache[[url, name, path]] ||= _clone(url, name, path)
30
64
  end
@@ -1,4 +1,4 @@
1
- require_relative 'git_hub_repository_cloner'
1
+ require_relative 'git_cloner'
2
2
  require_relative 'local_filesystem_cloner'
3
3
 
4
4
  module Bookbinder
@@ -10,11 +10,11 @@ module Bookbinder
10
10
  @version_control_system = version_control_system
11
11
  end
12
12
 
13
- def produce(source, user_repo_dir)
13
+ def produce(user_repo_dir)
14
14
  if user_repo_dir
15
15
  LocalFilesystemCloner.new(logger, filesystem, user_repo_dir)
16
16
  else
17
- GitHubRepositoryCloner.new(version_control_system)
17
+ GitCloner.new(version_control_system)
18
18
  end
19
19
  end
20
20
 
@@ -2,7 +2,13 @@ module Bookbinder
2
2
  module Ingest
3
3
  DestinationDirectory = Struct.new(:full_repo_name, :desired_destination_dir_name) do
4
4
  def to_str
5
- desired_destination_dir_name || full_repo_name.split('/')[1]
5
+ if desired_destination_dir_name
6
+ desired_destination_dir_name.to_s
7
+ elsif full_repo_name
8
+ full_repo_name.split('/').last
9
+ else
10
+ ""
11
+ end
6
12
  end
7
13
 
8
14
  alias :to_s :to_str
@@ -1,9 +1,10 @@
1
1
  require_relative 'destination_directory'
2
+ require_relative 'repo_identifier'
2
3
  require_relative 'working_copy'
3
4
 
4
5
  module Bookbinder
5
6
  module Ingest
6
- class GitHubRepositoryCloner
7
+ class GitCloner
7
8
  def initialize(version_control_system)
8
9
  @version_control_system = version_control_system
9
10
  end
@@ -15,7 +16,7 @@ module Bookbinder
15
16
  dest_dir = DestinationDirectory.new(source_repo_name, destination_dir_name)
16
17
  copied_to = Pathname(destination_parent_dir).join(dest_dir)
17
18
  version_control_system.clone(
18
- "git@github.com:#{source_repo_name}",
19
+ RepoIdentifier.new(source_repo_name),
19
20
  dest_dir,
20
21
  path: destination_parent_dir,
21
22
  checkout: source_ref
@@ -0,0 +1,45 @@
1
+ module Bookbinder
2
+ module Ingest
3
+ class RepoIdentifier
4
+ DEFAULT_VCS_PREFIX = 'git@github.com:'
5
+
6
+ def initialize(input_identifier)
7
+ @input_identifier = input_identifier
8
+ end
9
+
10
+ def to_str
11
+ if input_identifier.nil?
12
+ ""
13
+ elsif input_identifier.include?(':')
14
+ input_identifier
15
+ else
16
+ "#{DEFAULT_VCS_PREFIX}#{input_identifier}"
17
+ end
18
+ end
19
+
20
+ alias :to_s :to_str
21
+
22
+ def inspect
23
+ %Q("#{to_s}")
24
+ end
25
+
26
+ def split(*args)
27
+ input_identifier.split(*args)
28
+ end
29
+
30
+ def hash
31
+ to_str.hash
32
+ end
33
+
34
+ def ==(other)
35
+ to_str == other
36
+ end
37
+
38
+ alias :eql? :==
39
+
40
+ private
41
+
42
+ attr_reader :input_identifier
43
+ end
44
+ end
45
+ end
@@ -1,6 +1,7 @@
1
1
  require 'find'
2
2
  require 'pathname'
3
3
  require 'nokogiri'
4
+ require_relative 'errors/programmer_mistake'
4
5
 
5
6
  module Bookbinder
6
7
 
@@ -32,19 +33,13 @@ module Bookbinder
32
33
  end
33
34
 
34
35
  def copy(src, dest)
35
- unless File.directory?(dest)
36
- FileUtils.mkdir_p(dest)
37
- end
38
-
36
+ make_directory(dest)
39
37
  FileUtils.cp_r src, dest
40
38
  end
41
39
 
42
40
  def copy_contents(src, dest)
43
- unless File.directory?(dest)
44
- FileUtils.mkdir_p(dest)
45
- end
46
-
47
- FileUtils.cp_r "#{src}/.", dest
41
+ raise Errors::ProgrammerMistake.new("The method copy_contents cannot copy the contents of the directory '#{src}' because it was not found.") unless Dir.exists?(src)
42
+ copy "#{src}/.", dest
48
43
  end
49
44
 
50
45
  def copy_including_intermediate_dirs(file, root, dest)
@@ -1,13 +1,12 @@
1
+ require 'git/lib'
1
2
  require 'middleman-core'
2
3
  require 'middleman-core/cli'
3
4
  require 'middleman-core/profiling'
4
- require_relative 'values/code_example'
5
5
  require_relative 'code_example_reader'
6
6
 
7
7
  class Middleman::Cli::BuildAction
8
8
  def handle_error(file_name, response, e=Thor::Error.new(response))
9
- our_errors = [Bookbinder::GitClient::TokenException,
10
- Bookbinder::CodeExampleReader::InvalidSnippet,
9
+ our_errors = [Bookbinder::CodeExampleReader::InvalidSnippet,
11
10
  QuicklinksRenderer::BadHeadingLevelError,
12
11
  Git::GitExecuteError]
13
12
  raise e if our_errors.include?(e.class)
@@ -46,10 +45,10 @@ module Bookbinder
46
45
 
47
46
  within(output_locations.master_dir) do
48
47
  invoke_against_current_dir(output_locations.workspace_dir,
49
- config[:host_for_sitemap],
48
+ config.public_host,
50
49
  subnav_templates_by_directory,
51
- config.fetch(:template_variables, {}),
52
- config[:archive_menu],
50
+ config.template_variables,
51
+ config.archive_menu,
53
52
  verbose,
54
53
  cloner)
55
54
  end
@@ -0,0 +1,27 @@
1
+ module Bookbinder
2
+ module Preprocessing
3
+ class CopyToSiteGenDir
4
+ def initialize(filesystem)
5
+ @filesystem = filesystem
6
+ end
7
+
8
+ def applicable_to?(section)
9
+ section.subnav_template != 'dita_subnav' &&
10
+ filesystem.file_exist?(section.path_to_repository)
11
+ end
12
+
13
+ def preprocess(sections, output_locations, *_)
14
+ sections.each do |section|
15
+ filesystem.copy_contents(
16
+ section.path_to_repository,
17
+ output_locations.source_for_site_generator.join(section.desired_directory)
18
+ )
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :filesystem
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,103 @@
1
+ require_relative '../values/subnav'
2
+
3
+ module Bookbinder
4
+ module Preprocessing
5
+ class DitaPreprocessor
6
+ DitaToHtmlLibraryFailure = Class.new(RuntimeError)
7
+
8
+ ACCEPTED_IMAGE_FORMATS = %w(png jpeg jpg svg gif bmp tif tiff eps)
9
+
10
+ def initialize(dita_formatter, local_fs_accessor, command_creator, sheller)
11
+ @dita_formatter = dita_formatter
12
+ @local_fs_accessor = local_fs_accessor
13
+ @command_creator = command_creator
14
+ @sheller = sheller
15
+ end
16
+
17
+ def applicable_to?(section)
18
+ section.subnav_template == 'dita_subnav'
19
+ end
20
+
21
+ def preprocess(dita_sections, output_locations, options: nil, output_streams: nil)
22
+ dita_sections.select { |dita_section| dita_section.path_to_preprocessor_attribute('ditamap_location') }.each do |dita_section|
23
+ command = command_creator.convert_to_html_command(
24
+ dita_section,
25
+ dita_flags: dita_flags(options),
26
+ write_to: output_locations.html_from_preprocessing_dir.join(dita_section.desired_directory)
27
+ )
28
+ status = sheller.run_command(command, output_streams.to_h)
29
+ unless status.success?
30
+ raise DitaToHtmlLibraryFailure.new 'The DITA-to-HTML conversion failed. ' +
31
+ 'Please check that you have specified the path to your DITA-OT library in the ENV, ' +
32
+ 'that your DITA-specific keys/values in config.yml are set, ' +
33
+ 'and that your DITA toolkit is correctly configured.'
34
+ end
35
+
36
+ generate_subnav(dita_section.desired_directory,
37
+ output_locations,
38
+ output_locations.source_for_site_generator.join('subnavs', '_dita_subnav_template.erb'),
39
+ output_locations.subnavs_for_layout_dir)
40
+ end
41
+
42
+ dita_sections.each do |dita_section|
43
+ html_dir = output_locations.html_from_preprocessing_dir.join(dita_section.desired_directory)
44
+ formatted_dir = output_locations.formatted_dir.join(dita_section.desired_directory)
45
+ source_for_site_gen_dir = output_locations.source_for_site_generator.join(dita_section.desired_directory)
46
+
47
+ dita_formatter.format_html html_dir, formatted_dir
48
+
49
+ copy_images(dita_section.path_to_repository, formatted_dir)
50
+
51
+ local_fs_accessor.copy_contents(formatted_dir, source_for_site_gen_dir)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ attr_reader :dita_formatter, :local_fs_accessor, :command_creator, :sheller
58
+
59
+ def generate_subnav(dita_section_dir, output_locations, dita_subnav_template_path, subnavs_dir)
60
+ dita_subnav_template_text = local_fs_accessor.read(dita_subnav_template_path)
61
+
62
+ tocjs_text = local_fs_accessor.read(
63
+ File.join(
64
+ output_locations.html_from_preprocessing_dir.join(dita_section_dir),
65
+ 'index.html')
66
+ )
67
+ json_props_location = File.join('dita-subnav-props.json')
68
+ props_file_location = File.join(subnavs_dir, json_props_location)
69
+
70
+ subnav = dita_formatter.format_subnav(dita_section_dir,
71
+ dita_subnav_template_text,
72
+ json_props_location,
73
+ tocjs_text)
74
+
75
+ local_fs_accessor.write text: subnav.json_links, to: props_file_location
76
+
77
+ local_fs_accessor.write text: subnav.text,
78
+ to: File.join(subnavs_dir, "dita_subnav.erb")
79
+ end
80
+
81
+ def copy_images(src, dest)
82
+ image_paths = ACCEPTED_IMAGE_FORMATS.map do |format|
83
+ local_fs_accessor.find_files_with_ext(format, src)
84
+ end.flatten
85
+
86
+ image_paths.each do |image_path|
87
+ local_fs_accessor.copy_including_intermediate_dirs(image_path,
88
+ src,
89
+ dest)
90
+ end
91
+ end
92
+
93
+ def dita_flags(opts)
94
+ matching_flags = opts.map {|o| o[flag_value_regex("dita-flags"), 1] }
95
+ matching_flags.compact.first
96
+ end
97
+
98
+ def flag_value_regex(flag_name)
99
+ Regexp.new(/--#{flag_name}=(.+)/)
100
+ end
101
+ end
102
+ end
103
+ end