bookbindery 3.0.1 → 3.1.0

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