bookbindery 2.1.5 → 3.0.1

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