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,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