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,28 +1,45 @@
1
+ require_relative 'config/aws_credentials'
2
+ require_relative 'config/cf_credentials'
1
3
  require_relative 'configuration'
2
4
  require_relative 'yaml_loader'
3
5
 
4
6
  module Bookbinder
5
7
  class ConfigurationFetcher
6
- def initialize(logger, configuration_validator, loader)
8
+ def initialize(logger, configuration_validator, loader, credentials_provider)
7
9
  @loader = loader
8
10
  @logger = logger
9
11
  @configuration_validator = configuration_validator
12
+ @credentials_provider = credentials_provider
10
13
  end
11
14
 
12
15
  def fetch_config
13
16
  @config ||= validate(read_config_file)
14
17
  end
15
18
 
19
+ def fetch_credentials(environment = 'null-environment')
20
+ @credentials ||= credentials_provider.credentials("git@github.com:#{fetch_config.cred_repo}")
21
+ {
22
+ aws: Config::AwsCredentials.new(
23
+ @credentials.fetch('aws', {})
24
+ ),
25
+ cloud_foundry: Config::CfCredentials.new(
26
+ @credentials.fetch('cloud_foundry', {}),
27
+ environment
28
+ )
29
+ }
30
+ end
31
+
16
32
  def set_config_file_path config_file_path
17
33
  @config_file_path = File.expand_path config_file_path
18
34
  end
19
35
 
20
36
  private
21
37
 
22
- attr_reader(:loader, :logger, :configuration_validator, :config, :config_file_path)
38
+ attr_reader(:loader, :logger, :configuration_validator, :config, :config_file_path,
39
+ :credentials_provider)
23
40
 
24
41
  def read_config_file
25
- loader.load(config_file_path).merge('pdf_index' => nil)
42
+ loader.load(config_file_path)
26
43
 
27
44
  rescue FileNotFoundError => e
28
45
  raise "The configuration file specified does not exist. Please create a config #{e} file at #{config_file_path} and try again."
@@ -31,7 +48,14 @@ module Bookbinder
31
48
  end
32
49
 
33
50
  def validate(config_hash)
34
- Configuration.new(logger, config_hash) if configuration_validator.valid?(config_hash, Configuration::CURRENT_SCHEMA_VERSION, Configuration::STARTING_SCHEMA_VERSION)
51
+ raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
52
+
53
+ errors = configuration_validator.exceptions(config_hash,
54
+ Configuration::CURRENT_SCHEMA_VERSION,
55
+ Configuration::STARTING_SCHEMA_VERSION)
56
+ raise errors.first if errors.any?
57
+
58
+ Configuration.new(logger, config_hash)
35
59
  end
36
60
  end
37
61
  end
@@ -1,188 +1,35 @@
1
- require 'active_support/core_ext/object/blank'
1
+ require_relative 'validation_checkers/duplicate_section_name_checker'
2
+ require_relative 'validation_checkers/archive_menu_checker'
3
+ require_relative 'validation_checkers/config_version_checker'
4
+ require_relative 'validation_checkers/required_keys_checker'
5
+ require_relative 'validation_checkers/repository_name_presence_checker'
6
+ require_relative 'validation_checkers/dita_section_checker'
2
7
 
3
8
  module Bookbinder
4
9
  class ConfigurationValidator
5
- DuplicateSectionNameError = Class.new(RuntimeError)
6
- MissingArchiveMenuPartialError = Class.new(RuntimeError)
7
- EmptyArchiveItemsError = Class.new(RuntimeError)
8
- ArchiveMenuNotDefinedError = Class.new(RuntimeError)
9
- MissingRequiredKeyError = Class.new(RuntimeError)
10
-
11
10
  def initialize(logger, file_system_accessor)
12
11
  @logger = logger
13
12
  @file_system_accessor = file_system_accessor
14
13
  end
15
14
 
16
- def valid?(config_hash, bookbinder_schema_version, starting_schema_version)
17
- raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
18
-
15
+ def exceptions(config_hash, bookbinder_schema_version, starting_schema_version)
19
16
  user_config_schema_version = config_hash['schema_version']
20
17
  exceptions = [
21
- RequiredKeysChecker.new,
22
18
  ConfigVersionChecker.new(Version.parse(bookbinder_schema_version),
23
19
  Version.parse(starting_schema_version),
24
20
  VersionCheckerMessages.new(Version.parse(user_config_schema_version),
25
21
  bookbinder_schema_version),
26
22
  @logger),
23
+ RequiredKeysChecker.new,
27
24
  DuplicateSectionNameChecker.new,
25
+ RepositoryNamePresenceChecker.new,
26
+ DitaSectionChecker.new,
28
27
  ArchiveMenuChecker.new(@file_system_accessor)
29
28
  ].map do |checker|
30
29
  checker.check(config_hash)
31
30
  end
32
- exception = exceptions.compact.first
33
- raise exception if exception
34
-
35
- true
36
- end
37
- end
38
-
39
- class DuplicateSectionNameChecker
40
- def check(config)
41
- if duplicate_section_names?(config)
42
- ConfigurationValidator::DuplicateSectionNameError
43
- end
44
- end
45
-
46
- private
47
-
48
- def duplicate_section_names?(config)
49
- if config['sections']
50
- directory_names = config['sections'].map {|section| section['directory']}
51
- directory_names.length != directory_names.uniq.length
52
- else
53
- false
54
- end
55
- end
56
-
57
- end
58
31
 
59
- class ArchiveMenuChecker
60
- def initialize(file_system_accessor)
61
- @file_system_accessor = file_system_accessor
62
- end
63
-
64
- def check(config)
65
- partial_location = './master_middleman/source/archive_menus/_default.erb'
66
- if config.has_key?("archive_menu") && config["archive_menu"].nil?
67
- ConfigurationValidator::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.'
68
- elsif archive_items(config).include?(nil)
69
- ConfigurationValidator::EmptyArchiveItemsError.new 'Did you forget to add a value to the archive_menu?'
70
- elsif config.has_key?("archive_menu") && !@file_system_accessor.file_exist?(partial_location)
71
- ConfigurationValidator::MissingArchiveMenuPartialError.new "You must provide a template partial named at #{partial_location}"
72
- end
73
- end
74
-
75
- private
76
-
77
- def archive_items(config)
78
- config.fetch('archive_menu', [])
79
- end
80
-
81
- end
82
-
83
- Version = Struct.new(:major, :minor, :patch) do
84
- class << self
85
- def parse(raw_version)
86
- if raw_version
87
- new(*raw_version.split('.'))
88
- else
89
- new(nil, nil, nil)
90
- end
91
- end
92
- end
93
-
94
- def valid?
95
- [major, minor, patch].all?(&:present?)
96
- end
97
-
98
- def to_s
99
- [major, minor, patch].compact.join('.')
100
- end
101
- end
102
-
103
- class VersionCheckerMessages
104
- def initialize(user_schema_version, bookbinder_schema_version)
105
- @user_schema_version = user_schema_version
106
- @bookbinder_schema_version = bookbinder_schema_version
107
- end
108
-
109
- def schema_now_required_message
110
- "[ERROR] Bookbinder now requires a certain schema. Please see README " +
111
- "and provide a schema version."
112
- end
113
-
114
- def incompatible_schema_message
115
- "[ERROR] Your config.yml format, schema version #{user_schema_version}, " +
116
- "is older than this version of Bookbinder can support. Please update " +
117
- "your config.yml keys and format to version #{bookbinder_schema_version} " +
118
- "and try again."
119
- end
120
-
121
- def unrecognized_schema_version_message
122
- "[ERROR] The config schema version #{user_schema_version} is " +
123
- "unrecognized by this version of Bookbinder. The latest schema version " +
124
- "is #{bookbinder_schema_version}."
125
- end
126
-
127
- private
128
-
129
- attr_reader :user_schema_version, :bookbinder_schema_version
130
- end
131
-
132
- class ConfigVersionChecker
133
- def initialize(bookbinder_schema_version, starting_schema_version, messages, logger)
134
- @bookbinder_schema_version = bookbinder_schema_version
135
- @starting_schema_version = starting_schema_version
136
- @messages = messages
137
- @logger = logger
138
- end
139
-
140
- def check(config)
141
- user_schema_version = Version.parse(config['schema_version'])
142
- if user_schema_version.valid?
143
- if user_schema_version.major > bookbinder_schema_version.major
144
- raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
145
- elsif user_schema_version.minor > bookbinder_schema_version.minor
146
- raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
147
- elsif user_schema_version.patch > bookbinder_schema_version.patch
148
- raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
149
- elsif user_schema_version.major < bookbinder_schema_version.major
150
- raise Configuration::ConfigSchemaUnsupportedError.new messages.incompatible_schema_message
151
- elsif user_schema_version.minor < bookbinder_schema_version.minor
152
- @logger.warn nonbreaking_schema_message_for("minor")
153
- elsif user_schema_version.patch < bookbinder_schema_version.patch
154
- @logger.warn nonbreaking_schema_message_for("patch")
155
- end
156
- elsif bookbinder_schema_version != starting_schema_version
157
- raise Configuration::ConfigSchemaUnsupportedError.new messages.schema_now_required_message
158
- end
159
- end
160
-
161
- private
162
-
163
- attr_reader :bookbinder_schema_version, :starting_schema_version, :messages
164
-
165
- def nonbreaking_schema_message_for(version_level)
166
- "[WARNING] Your schema is valid, but there exists a new #{version_level} version. Consider updating your config.yml."
167
- end
168
- end
169
-
170
- class RequiredKeysChecker
171
- def check(config)
172
- missing_keys = []
173
-
174
- Configuration::CONFIG_REQUIRED_KEYS.map do |required_key|
175
- config_keys = config.keys
176
- unless config_keys.include?(required_key)
177
- missing_keys.push(required_key)
178
- end
179
- end
180
-
181
- if missing_keys.length > 0
182
- raise ConfigurationValidator::MissingRequiredKeyError.new(
183
- "Your config.yml is missing required key(s). Required keys are #{missing_keys.join(", ")}."
184
- )
185
- end
32
+ exceptions.compact
186
33
  end
187
34
  end
188
35
  end
@@ -1,16 +1,21 @@
1
1
  require_relative 'app_fetcher'
2
+ require_relative 'archive'
2
3
  require_relative 'artifact_namer'
4
+ require_relative 'cf_command_runner'
5
+ require_relative 'ingest/destination_directory'
6
+ require_relative 'pusher'
7
+ require_relative 'sheller'
3
8
 
4
9
  module Bookbinder
5
10
  class Distributor
6
11
  EXPIRATION_HOURS = 2
7
12
 
8
13
  def self.build(logger, options)
9
- namespace = GitHubRepository.new(logger: logger, full_name: options[:book_repo], git_accessor: Git).short_name
14
+ namespace = Ingest::DestinationDirectory.new(options[:book_repo], nil)
10
15
  namer = ArtifactNamer.new(namespace, options[:build_number], 'log', '/tmp')
11
16
 
12
17
  archive = Archive.new(logger: logger, key: options[:aws_credentials].access_key, secret: options[:aws_credentials].secret_key)
13
- cf_command_runner = CfCommandRunner.new(logger, options[:cf_credentials], namer.full_path)
18
+ cf_command_runner = CfCommandRunner.new(logger, Sheller.new, options[:cf_credentials], namer.full_path)
14
19
  cf_app_fetcher = AppFetcher.new(options[:cf_credentials].flat_routes, cf_command_runner)
15
20
 
16
21
  pusher = Pusher.new(cf_command_runner, cf_app_fetcher)
@@ -27,22 +32,21 @@ module Bookbinder
27
32
  end
28
33
 
29
34
  def distribute
30
- begin
31
- download if cf_credentials.download_archive_before_push?
32
- push_app
33
- nil
34
- rescue => e
35
- @logger.error(<<-ERROR.chomp)
35
+ download if cf_credentials.download_archive_before_push?
36
+ push_app
37
+ nil
38
+ rescue => e
39
+ @logger.error(<<-ERROR.chomp)
36
40
  [ERROR] #{e.message}
37
41
  [DEBUG INFO]
38
42
  CF organization: #{cf_credentials.organization}
39
43
  CF space: #{cf_credentials.space}
40
44
  CF account: #{cf_credentials.username}
41
45
  routes: #{cf_credentials.routes}
42
- ERROR
43
- ensure
44
- upload_trace
45
- end
46
+ ERROR
47
+ raise
48
+ ensure
49
+ upload_trace
46
50
  end
47
51
 
48
52
  private
@@ -1,15 +1,12 @@
1
1
  require_relative '../bookbinder/values/dita_section'
2
2
 
3
3
  module Bookbinder
4
- class DitaToHtmlConverter
5
- DitaToHtmlLibraryFailure = Class.new(RuntimeError)
6
-
7
- def initialize(sheller, path_to_dita_ot_library)
8
- @sheller = sheller
4
+ class DitaCommandCreator
5
+ def initialize(path_to_dita_ot_library)
9
6
  @path_to_dita_ot_library = path_to_dita_ot_library
10
7
  end
11
8
 
12
- def convert_to_html(dita_section, write_to: nil)
9
+ def convert_to_html_command(dita_section, write_to: nil)
13
10
  classpath = "#{path_to_dita_ot_library}/lib/xercesImpl.jar:" +
14
11
  "#{path_to_dita_ot_library}/lib/xml-apis.jar:" +
15
12
  "#{path_to_dita_ot_library}/lib/resolver.jar:" +
@@ -32,20 +29,11 @@ module Bookbinder
32
29
  if dita_section.absolute_path_to_ditaval
33
30
  command += "-Dargs.filter=#{dita_section.absolute_path_to_ditaval} "
34
31
  end
35
-
36
- begin
37
- sheller.run_command(command)
38
- rescue Sheller::ShelloutFailure
39
- raise DitaToHtmlLibraryFailure.new 'The DITA-to-HTML conversion failed. ' +
40
- 'Please check that you have specified the path to your DITA-OT library in the ENV, ' +
41
- 'that your DITA-specific keys/values in config.yml are set, ' +
42
- 'and that your DITA toolkit is correctly configured.'
43
-
44
- end
32
+ command
45
33
  end
46
34
 
47
35
  private
48
36
 
49
- attr_reader :sheller, :path_to_dita_ot_library
37
+ attr_reader :path_to_dita_ot_library
50
38
  end
51
39
  end
@@ -5,32 +5,34 @@ module Bookbinder
5
5
 
6
6
  ACCEPTED_IMAGE_FORMATS = %w(png jpeg jpg svg gif bmp tif tiff eps)
7
7
 
8
- def initialize(dita_converter, dita_formatter, local_fs_accessor)
9
- @dita_converter = dita_converter
8
+ def initialize(dita_formatter, local_fs_accessor)
10
9
  @dita_formatter = dita_formatter
11
10
  @local_fs_accessor = local_fs_accessor
12
11
  end
13
12
 
14
- def preprocess(dita_section,
13
+ def preprocess(dita_sections,
15
14
  subnavs_dir,
16
- dita_subnav_template_path)
17
- if dita_section.ditamap_location
18
- dita_converter.convert_to_html dita_section, write_to: dita_section.html_from_dita_section_dir
19
-
15
+ dita_subnav_template_path,
16
+ &block)
17
+ ditamap_location_sections = dita_sections.select { |dita_section| dita_section.ditamap_location }
18
+ ditamap_location_sections.each do |dita_section|
19
+ block.call(dita_section)
20
20
  generate_subnav(dita_section, dita_subnav_template_path, subnavs_dir)
21
21
  end
22
22
 
23
- dita_formatter.format_html dita_section.html_from_dita_section_dir, dita_section.formatted_section_dir
23
+ dita_sections.each do |dita_section|
24
+ dita_formatter.format_html dita_section.html_from_dita_section_dir, dita_section.formatted_section_dir
24
25
 
25
- copy_images(dita_section.path_to_local_repo, dita_section.formatted_section_dir)
26
+ copy_images(dita_section.path_to_local_repo, dita_section.formatted_section_dir)
26
27
 
27
- local_fs_accessor.copy_contents(dita_section.formatted_section_dir,
28
- dita_section.section_source_for_site_generator)
28
+ local_fs_accessor.copy_contents(dita_section.formatted_section_dir,
29
+ dita_section.section_source_for_site_generator)
30
+ end
29
31
  end
30
32
 
31
33
  private
32
34
 
33
- attr_reader :dita_converter, :dita_formatter, :local_fs_accessor
35
+ attr_reader :dita_formatter, :local_fs_accessor
34
36
 
35
37
  def generate_subnav(dita_section, dita_subnav_template_path, subnavs_dir)
36
38
  dita_subnav_template_text = local_fs_accessor.read(dita_subnav_template_path)
@@ -6,12 +6,31 @@ module Bookbinder
6
6
  @cache = {}
7
7
  end
8
8
 
9
- def clone(url, name, path: nil)
10
- cache[[url, name, path]] ||= Git.clone(url, name, path: path)
9
+ def clone(url, name, path: nil, checkout: 'master')
10
+ cached_clone(url, name, path).tap do |git|
11
+ git.checkout(checkout)
12
+ end
13
+ end
14
+
15
+ def read_file(filename, from_repo: nil, checkout: 'master')
16
+ Dir.mktmpdir do |dir|
17
+ path = Pathname(dir)
18
+ git = _clone(from_repo, "read-file", path)
19
+ git.checkout(checkout)
20
+ path.join("read-file", filename).read
21
+ end
11
22
  end
12
23
 
13
24
  private
14
25
 
15
26
  attr_reader :cache
27
+
28
+ def cached_clone(url, name, path)
29
+ cache[[url, name, path]] ||= _clone(url, name, path)
30
+ end
31
+
32
+ def _clone(url, name, path)
33
+ Git.clone(url, name, path: path)
34
+ end
16
35
  end
17
36
  end
@@ -11,32 +11,6 @@ module Bookbinder
11
11
 
12
12
  attr_reader :full_name, :copied_to
13
13
 
14
- def self.build_from_remote(logger,
15
- section_hash,
16
- git_accessor)
17
- full_name = section_hash.fetch('repository', {}).fetch('name')
18
- directory = section_hash['directory']
19
- new(logger: logger,
20
- full_name: full_name,
21
- github_token: ENV['GITHUB_API_TOKEN'],
22
- directory: directory,
23
- git_accessor: git_accessor)
24
- end
25
-
26
- def self.build_from_local(logger,
27
- section_hash,
28
- local_repo_dir,
29
- git_accessor)
30
- full_name = section_hash.fetch('repository').fetch('name')
31
- directory = section_hash['directory']
32
-
33
- new(logger: logger,
34
- full_name: full_name,
35
- directory: directory,
36
- local_repo_dir: local_repo_dir,
37
- git_accessor: git_accessor)
38
- end
39
-
40
14
  def initialize(logger: nil,
41
15
  full_name: nil,
42
16
  github_token: nil,
@@ -89,23 +63,6 @@ module Bookbinder
89
63
  @copied_to = File.join(destination_dir, directory)
90
64
  end
91
65
 
92
- def copy_from_local(destination_dir)
93
- if File.exist?(path_to_local_repo)
94
- @logger.log ' copying '.yellow + path_to_local_repo
95
- destination = File.join(destination_dir, directory)
96
-
97
- if !destination.include?(path_to_local_repo) && !File.exists?(destination)
98
- FileUtils.mkdir_p(destination)
99
-
100
- FileUtils.cp_r(File.join(path_to_local_repo, '.'), destination)
101
- end
102
-
103
- @copied_to = File.join(destination_dir, directory)
104
- else
105
- announce_skip
106
- end
107
- end
108
-
109
66
  def copied?
110
67
  !@copied_to.nil?
111
68
  end