bookbindery 2.1.5 → 3.0.1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bookbinder.rb +0 -4
  3. data/lib/bookbinder/cf_command_runner.rb +11 -5
  4. data/lib/bookbinder/cli.rb +8 -12
  5. data/lib/bookbinder/colorizer.rb +1 -2
  6. data/lib/bookbinder/command_runner.rb +0 -2
  7. data/lib/bookbinder/commands/bind.rb +96 -99
  8. data/lib/bookbinder/commands/bind/directory_preparer.rb +60 -0
  9. data/lib/bookbinder/commands/build_and_push_tarball.rb +5 -4
  10. data/lib/bookbinder/commands/push_from_local.rb +56 -2
  11. data/lib/bookbinder/commands/push_to_prod.rb +6 -2
  12. data/lib/bookbinder/commands/tag.rb +15 -3
  13. data/lib/bookbinder/config/aws_credentials.rb +21 -0
  14. data/lib/bookbinder/config/cf_credentials.rb +87 -0
  15. data/lib/bookbinder/configuration.rb +4 -140
  16. data/lib/bookbinder/configuration_fetcher.rb +28 -4
  17. data/lib/bookbinder/configuration_validator.rb +11 -164
  18. data/lib/bookbinder/distributor.rb +16 -12
  19. data/lib/bookbinder/{dita_to_html_converter.rb → dita_command_creator.rb} +5 -17
  20. data/lib/bookbinder/dita_preprocessor.rb +14 -12
  21. data/lib/bookbinder/git_accessor.rb +21 -2
  22. data/lib/bookbinder/git_hub_repository.rb +0 -43
  23. data/lib/bookbinder/ingest/cloner_factory.rb +5 -4
  24. data/lib/bookbinder/ingest/destination_directory.rb +15 -0
  25. data/lib/bookbinder/ingest/git_hub_repository_cloner.rb +22 -13
  26. data/lib/bookbinder/ingest/local_filesystem_cloner.rb +38 -14
  27. data/lib/bookbinder/ingest/working_copy.rb +31 -0
  28. data/lib/bookbinder/local_dita_section_gatherer.rb +4 -3
  29. data/lib/bookbinder/local_file_system_accessor.rb +1 -4
  30. data/lib/bookbinder/middleman_runner.rb +22 -26
  31. data/lib/bookbinder/remote_yaml_credential_provider.rb +10 -10
  32. data/lib/bookbinder/repositories/command_repository.rb +18 -12
  33. data/lib/bookbinder/repositories/section_repository.rb +8 -8
  34. data/lib/bookbinder/server_director.rb +16 -33
  35. data/lib/bookbinder/sheller.rb +33 -7
  36. data/lib/bookbinder/spider.rb +3 -0
  37. data/lib/bookbinder/streams/switchable_stdout_and_red_stderr.rb +38 -0
  38. data/lib/bookbinder/terminal.rb +11 -1
  39. data/lib/bookbinder/validation_checkers/archive_menu_checker.rb +31 -0
  40. data/lib/bookbinder/validation_checkers/config_version_checker.rb +91 -0
  41. data/lib/bookbinder/validation_checkers/dita_section_checker.rb +20 -0
  42. data/lib/bookbinder/validation_checkers/duplicate_section_name_checker.rb +25 -0
  43. data/lib/bookbinder/validation_checkers/repository_name_presence_checker.rb +33 -0
  44. data/lib/bookbinder/validation_checkers/required_keys_checker.rb +43 -0
  45. data/lib/bookbinder/values/dita_section.rb +5 -1
  46. data/lib/bookbinder/values/output_locations.rb +25 -2
  47. data/lib/bookbinder/values/user_message.rb +2 -2
  48. data/master_middleman/bookbinder_helpers.rb +5 -15
  49. data/template_app/Gemfile.lock +1 -1
  50. metadata +60 -80
  51. data/lib/bookbinder/commands/generate_pdf.rb +0 -141
  52. data/lib/bookbinder/pdf_generator.rb +0 -73
  53. data/lib/bookbinder/publisher.rb +0 -58
  54. data/lib/bookbinder/user_message_presenter.rb +0 -21
@@ -1,4 +1,6 @@
1
- require 'popen4'
1
+ require 'puma'
2
+ require 'rack/rewrite'
3
+ require 'vienna'
2
4
 
3
5
  module Bookbinder
4
6
  class ServerDirector
@@ -10,16 +12,14 @@ module Bookbinder
10
12
 
11
13
  def use_server
12
14
  Dir.chdir(@directory) do
13
- result = nil
14
- POpen4::popen4("puma -p #{@port}") do |stdout, stderr, stdin, pid|
15
- begin
16
- wait_for_server(stdout)
17
- consume_stream_in_separate_thread(stdout)
18
- consume_stream_in_separate_thread(stderr)
19
- result = yield @port
20
- ensure
21
- stop_server(pid)
22
- end
15
+ events = Puma::Events.new $stdout, $stderr
16
+ server = Puma::Server.new app, events
17
+ server.add_tcp_listener "localhost", @port
18
+ server.run
19
+ begin
20
+ result = yield @port
21
+ ensure
22
+ server.stop(true)
23
23
  end
24
24
  result
25
25
  end
@@ -27,28 +27,11 @@ module Bookbinder
27
27
 
28
28
  private
29
29
 
30
- def wait_for_server(io)
31
- Kernel.sleep(1)
32
- begin
33
- line = io.gets
34
- raise 'Puma could not start' if line.nil?
35
-
36
- @logger.log "Vienna says, #{line}"
37
- end until line.include?('Listening on')
38
-
39
- @logger.log 'Vienna is lovely this time of year.'
40
- end
41
-
42
- def stop_server(pid)
43
- Process.kill 'KILL', pid
44
- end
45
-
46
- # avoids deadlocks by ensuring rack doesn't hang waiting to write to stderr
47
- def consume_stream_in_separate_thread(stream)
48
- Thread.new do
49
- s = nil
50
- while stream.read(1024, s)
51
- end
30
+ def app
31
+ if File.exists?('redirects.rb')
32
+ Rack::Rewrite.new(Vienna) { eval(File.read('redirects.rb')) }
33
+ else
34
+ Vienna
52
35
  end
53
36
  end
54
37
  end
@@ -1,17 +1,43 @@
1
+ require 'open3'
2
+
1
3
  module Bookbinder
2
4
  class Sheller
3
5
  ShelloutFailure = Class.new(RuntimeError)
4
6
 
5
- def initialize(view_updater)
6
- @view_updater = view_updater
7
+ class DevNull
8
+ def puts(_)
9
+ end
7
10
  end
8
11
 
9
- def run_command(command)
10
- IO.popen(command) do |stdout|
11
- stdout.each { |line| view_updater.log line }
12
- end
12
+ def run_command(*command)
13
+ out, err =
14
+ if Hash === command.last
15
+ command.last.values_at(:out, :err)
16
+ else
17
+ [DevNull.new, DevNull.new]
18
+ end
13
19
 
14
- raise ShelloutFailure.new "Shelling out failed." unless $?.success?
20
+ env_vars, executable =
21
+ if Hash === command.first
22
+ command[0..1]
23
+ else
24
+ [{}, command[0]]
25
+ end
26
+
27
+ exit_status = nil
28
+ Open3.popen3(env_vars, executable) do |stdin, stdout, stderr, wait_thr|
29
+ t = Thread.new do
30
+ stdout.each do |line|
31
+ out.puts(line)
32
+ end
33
+ end
34
+ stderr.each do |line|
35
+ err.puts(line)
36
+ end
37
+ t.join
38
+ exit_status = wait_thr.value
39
+ end
40
+ exit_status
15
41
  end
16
42
 
17
43
  attr_reader :view_updater
@@ -79,6 +79,9 @@ module Bookbinder
79
79
  Anemone.crawl(url) do |anemone|
80
80
  dont_visit_fragments(anemone)
81
81
  anemone.on_every_page do |page|
82
+ if $sitemap_debug
83
+ puts "\n\n********** Visiting page: #{page.url}\n\n#{page.body}"
84
+ end
82
85
  broken, working = sieve.links_from Stabilimentum.new(page), is_first_pass
83
86
  broken_links.concat broken
84
87
  sitemap.concat working
@@ -0,0 +1,38 @@
1
+ require_relative '../colorizer'
2
+
3
+ module Bookbinder
4
+ module Streams
5
+ class SwitchableStdoutAndRedStderr
6
+ def initialize(options)
7
+ @options = options
8
+ end
9
+
10
+ def to_h
11
+ {
12
+ out: options.include?('--verbose') ? $stdout : Sheller::DevNull.new,
13
+ err: ColorizedStream.new(Colorizer::Colors.red, $stderr)
14
+ }
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :options
20
+ end
21
+
22
+ class ColorizedStream
23
+ def initialize(color, stream)
24
+ @color = color
25
+ @stream = stream
26
+ @colorizer = Colorizer.new
27
+ end
28
+
29
+ def puts(line)
30
+ stream << colorizer.colorize(line, color)
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :color, :colorizer, :stream
36
+ end
37
+ end
38
+ end
@@ -3,8 +3,18 @@ require_relative 'colorizer'
3
3
 
4
4
  module Bookbinder
5
5
  class Terminal
6
+ def initialize(colorizer)
7
+ @colorizer = colorizer
8
+ end
9
+
6
10
  def update(user_message)
7
- puts user_message
11
+ if user_message.error?
12
+ error_message = @colorizer.colorize(user_message.message, Colorizer::Colors.red)
13
+ $stderr.puts error_message
14
+ elsif user_message.warn?
15
+ warning_message = @colorizer.colorize(user_message.message, Colorizer::Colors.yellow)
16
+ puts warning_message
17
+ end
8
18
  end
9
19
  end
10
20
  end
@@ -0,0 +1,31 @@
1
+ module Bookbinder
2
+ class ArchiveMenuChecker
3
+ MissingArchiveMenuPartialError = Class.new(RuntimeError)
4
+ ArchiveMenuNotDefinedError = Class.new(RuntimeError)
5
+ EmptyArchiveItemsError = Class.new(RuntimeError)
6
+
7
+ def initialize(file_system_accessor)
8
+ @file_system_accessor = file_system_accessor
9
+ end
10
+
11
+ def check(config)
12
+ partial_location = './master_middleman/source/archive_menus/_default.erb'
13
+ if config.has_key?("archive_menu") && config["archive_menu"].nil?
14
+ ArchiveMenuNotDefinedError.new 'Did you mean to provide an archive menu value to display? If you use the archive_menu key, you must provide at least one value.'
15
+ elsif archive_items(config).include?(nil)
16
+ EmptyArchiveItemsError.new 'Did you forget to add a value to the archive_menu?'
17
+ elsif config.has_key?("archive_menu") && !file_system_accessor.file_exist?(partial_location)
18
+ MissingArchiveMenuPartialError.new "You must provide a template partial named at #{partial_location}"
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :file_system_accessor
25
+
26
+ def archive_items(config)
27
+ config.fetch('archive_menu', [])
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,91 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require_relative '../configuration'
3
+
4
+ module Bookbinder
5
+ class ConfigVersionChecker
6
+ def initialize(bookbinder_schema_version, starting_schema_version, messages, view_updater)
7
+ @bookbinder_schema_version = bookbinder_schema_version
8
+ @starting_schema_version = starting_schema_version
9
+ @messages = messages
10
+ @view_updater = view_updater
11
+ end
12
+
13
+ def check(config)
14
+ user_schema_version = Version.parse(config['schema_version'])
15
+ if user_schema_version.valid?
16
+ if user_schema_version.major > bookbinder_schema_version.major
17
+ raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
18
+ elsif user_schema_version.minor > bookbinder_schema_version.minor
19
+ raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
20
+ elsif user_schema_version.patch > bookbinder_schema_version.patch
21
+ raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
22
+ elsif user_schema_version.major < bookbinder_schema_version.major
23
+ raise Configuration::ConfigSchemaUnsupportedError.new messages.incompatible_schema_message
24
+ elsif user_schema_version.minor < bookbinder_schema_version.minor
25
+ view_updater.warn nonbreaking_schema_message_for("minor")
26
+ elsif user_schema_version.patch < bookbinder_schema_version.patch
27
+ view_updater.warn nonbreaking_schema_message_for("patch")
28
+ end
29
+ elsif bookbinder_schema_version != starting_schema_version
30
+ raise Configuration::ConfigSchemaUnsupportedError.new messages.schema_now_required_message
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :bookbinder_schema_version, :starting_schema_version, :messages, :view_updater
37
+
38
+ def nonbreaking_schema_message_for(version_level)
39
+ "[WARNING] Your schema is valid, but there exists a new #{version_level} version. Consider updating your config.yml."
40
+ end
41
+ end
42
+
43
+ Version = Struct.new(:major, :minor, :patch) do
44
+ class << self
45
+ def parse(raw_version)
46
+ if raw_version
47
+ new(*raw_version.split('.'))
48
+ else
49
+ new(nil, nil, nil)
50
+ end
51
+ end
52
+ end
53
+
54
+ def valid?
55
+ [major, minor, patch].all?(&:present?)
56
+ end
57
+
58
+ def to_s
59
+ [major, minor, patch].compact.join('.')
60
+ end
61
+ end
62
+
63
+ class VersionCheckerMessages
64
+ def initialize(user_schema_version, bookbinder_schema_version)
65
+ @user_schema_version = user_schema_version
66
+ @bookbinder_schema_version = bookbinder_schema_version
67
+ end
68
+
69
+ def schema_now_required_message
70
+ "[ERROR] Bookbinder now requires a certain schema. Please see README " +
71
+ "and provide a schema version."
72
+ end
73
+
74
+ def incompatible_schema_message
75
+ "[ERROR] Your config.yml format, schema version #{user_schema_version}, " +
76
+ "is older than this version of Bookbinder can support. Please update " +
77
+ "your config.yml keys and format to version #{bookbinder_schema_version} " +
78
+ "and try again."
79
+ end
80
+
81
+ def unrecognized_schema_version_message
82
+ "[ERROR] The config schema version #{user_schema_version} is " +
83
+ "unrecognized by this version of Bookbinder. The latest schema version " +
84
+ "is #{bookbinder_schema_version}."
85
+ end
86
+
87
+ private
88
+
89
+ attr_reader :user_schema_version, :bookbinder_schema_version
90
+ end
91
+ end
@@ -0,0 +1,20 @@
1
+ module Bookbinder
2
+ class DitaSectionChecker
3
+ DitamapLocationError = Class.new(RuntimeError)
4
+
5
+ def check(config)
6
+ dita_sections = config['dita_sections'].to_a
7
+
8
+ sum = 0
9
+ dita_sections.each do |section|
10
+ if section['ditamap_location']
11
+ sum += 1
12
+ end
13
+ end
14
+
15
+ if !dita_sections.empty? && (sum < 1 || sum > 1)
16
+ DitamapLocationError.new "You must have exactly one 'ditamap_location' key in dita_sections."
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ module Bookbinder
2
+ class DuplicateSectionNameChecker
3
+ DuplicateSectionNameError = Class.new(RuntimeError)
4
+
5
+ def check(config)
6
+ if duplicate_section_names?(config)
7
+ DuplicateSectionNameError.new error_message
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def duplicate_section_names?(config)
14
+ sections = config['sections'].to_a + config['dita_sections'].to_a
15
+ directory_names = sections.map {|section| section['directory']}
16
+ directory_names.length != directory_names.uniq.length
17
+ end
18
+
19
+ def error_message
20
+ <<-ERROR
21
+ Duplicate repository names are not allowed.
22
+ ERROR
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ module Bookbinder
2
+ class RepositoryNamePresenceChecker
3
+ MissingRepositoryNameError = Class.new(RuntimeError)
4
+
5
+ def check(config)
6
+ all_sections = config['sections'].to_a + config['dita_sections'].to_a
7
+ failures = all_sections.map do |section|
8
+ if !section['repository'] || !section['repository']['name']
9
+ true
10
+ end
11
+ end
12
+
13
+ if failures.compact.empty?
14
+ nil
15
+ else
16
+ MissingRepositoryNameError.new error_message
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def error_message
23
+ <<-ERROR
24
+ Cannot locate a specific section.
25
+ All sections must provide the section 'name' key under the 'repository' key:
26
+
27
+ sections:
28
+ - repository:
29
+ name: 'your-org/your-repo'
30
+ ERROR
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ require_relative '../configuration'
2
+
3
+ module Bookbinder
4
+ class RequiredKeysChecker
5
+ MissingRequiredKeyError = Class.new(RuntimeError)
6
+ SectionAbsenceError = Class.new(RuntimeError)
7
+
8
+ def check(config)
9
+ missing_keys = []
10
+
11
+ Configuration::CONFIG_REQUIRED_KEYS.map do |required_key|
12
+ config_keys = config.keys
13
+ unless config_keys.include?(required_key)
14
+ missing_keys.push(required_key)
15
+ end
16
+ end
17
+
18
+ if missing_keys.length > 0
19
+ MissingRequiredKeyError.new("Your config.yml is missing required key(s). Required keys are #{missing_keys.join(", ")}.")
20
+ elsif !config['sections'] && !config['dita_sections']
21
+ SectionAbsenceError.new error_message
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def error_message
28
+ <<-ERROR
29
+ Cannot locate your sections.
30
+ Must specify at least one of 'sections' and/or 'dita_sections' in config.yml:
31
+
32
+ sections:
33
+ - repository:
34
+ name: 'your-org/your-repo'
35
+
36
+ dita_sections:
37
+ - repository:
38
+ name: 'dita-org/dita-repo'
39
+ ditamap_location: 'example.ditamap'
40
+ ERROR
41
+ end
42
+ end
43
+ end
@@ -4,7 +4,7 @@ module Bookbinder
4
4
  :ditaval_location,
5
5
  :full_name,
6
6
  :target_ref,
7
- :directory,
7
+ :directory_name,
8
8
  :output_locations) do
9
9
  def subnav
10
10
  namespace = directory.gsub('/', '_')
@@ -31,5 +31,9 @@ module Bookbinder
31
31
  def absolute_path_to_ditaval
32
32
  ditaval_location ? File.join(path_to_local_repo, ditaval_location) : nil
33
33
  end
34
+
35
+ def directory
36
+ directory_name || full_name.split('/').last
37
+ end
34
38
  end
35
39
  end