bookbindery 3.1.0 → 3.1.2

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/install_bin/bookbinder +1 -1
  3. data/lib/bookbinder/cf_command_runner.rb +1 -1
  4. data/lib/bookbinder/cli.rb +5 -5
  5. data/lib/bookbinder/code_example_reader.rb +57 -19
  6. data/lib/bookbinder/colorizer.rb +5 -2
  7. data/lib/bookbinder/commands/bind.rb +32 -42
  8. data/lib/bookbinder/commands/bind/bind_options.rb +6 -4
  9. data/lib/bookbinder/commands/bind/directory_preparer.rb +8 -13
  10. data/lib/bookbinder/{repositories/command_repository.rb → commands/collection.rb} +19 -37
  11. data/lib/bookbinder/config/archive_menu_configuration.rb +38 -0
  12. data/lib/bookbinder/config/checkers/archive_menu_checker.rb +35 -0
  13. data/lib/bookbinder/config/checkers/dita_section_checker.rb +24 -0
  14. data/lib/bookbinder/config/checkers/duplicate_section_name_checker.rb +29 -0
  15. data/lib/bookbinder/config/checkers/repository_name_presence_checker.rb +37 -0
  16. data/lib/bookbinder/config/checkers/required_keys_checker.rb +47 -0
  17. data/lib/bookbinder/config/configuration.rb +99 -0
  18. data/lib/bookbinder/config/fetcher.rb +61 -0
  19. data/lib/bookbinder/config/remote_yaml_credential_provider.rb +22 -0
  20. data/lib/bookbinder/config/validator.rb +30 -0
  21. data/lib/bookbinder/config/yaml_loader.rb +34 -0
  22. data/lib/bookbinder/ingest/git_cloner.rb +0 -1
  23. data/lib/bookbinder/ingest/local_filesystem_cloner.rb +22 -17
  24. data/lib/bookbinder/ingest/missing_working_copy.rb +21 -0
  25. data/lib/bookbinder/{repositories → ingest}/section_repository.rb +3 -4
  26. data/lib/bookbinder/{repositories → ingest}/section_repository_factory.rb +1 -1
  27. data/lib/bookbinder/ingest/working_copy.rb +12 -16
  28. data/lib/bookbinder/preprocessing/copy_to_site_gen_dir.rb +2 -3
  29. data/lib/bookbinder/preprocessing/dita_preprocessor.rb +5 -5
  30. data/lib/bookbinder/preprocessing/preprocessor.rb +1 -1
  31. data/lib/bookbinder/sheller.rb +3 -0
  32. data/lib/bookbinder/streams/colorized_stream.rb +1 -1
  33. data/lib/bookbinder/values/cf_routes.rb +17 -6
  34. data/lib/bookbinder/values/output_locations.rb +1 -12
  35. data/lib/bookbinder/values/section.rb +2 -7
  36. data/master_middleman/bookbinder_helpers.rb +23 -18
  37. data/master_middleman/quicklinks_renderer.rb +1 -0
  38. data/template_app/lib/rack_static.rb +2 -0
  39. data/template_app/lib/vienna_application.rb +2 -0
  40. metadata +51 -52
  41. data/lib/bookbinder.rb +0 -44
  42. data/lib/bookbinder/archive_menu_configuration.rb +0 -34
  43. data/lib/bookbinder/configuration.rb +0 -97
  44. data/lib/bookbinder/configuration_fetcher.rb +0 -59
  45. data/lib/bookbinder/configuration_validator.rb +0 -28
  46. data/lib/bookbinder/remote_yaml_credential_provider.rb +0 -20
  47. data/lib/bookbinder/shell_out.rb +0 -20
  48. data/lib/bookbinder/validation_checkers/archive_menu_checker.rb +0 -31
  49. data/lib/bookbinder/validation_checkers/dita_section_checker.rb +0 -20
  50. data/lib/bookbinder/validation_checkers/duplicate_section_name_checker.rb +0 -25
  51. data/lib/bookbinder/validation_checkers/repository_name_presence_checker.rb +0 -33
  52. data/lib/bookbinder/validation_checkers/required_keys_checker.rb +0 -43
  53. data/lib/bookbinder/yaml_loader.rb +0 -33
@@ -0,0 +1,38 @@
1
+ require_relative 'configuration'
2
+
3
+ module Bookbinder
4
+ module Config
5
+ class ArchiveMenuConfiguration
6
+ def initialize(loader: nil, config_filename: nil)
7
+ @loader = loader
8
+ @config_filename = config_filename
9
+ end
10
+
11
+ def generate(base_config, sections)
12
+ base_config.merge(
13
+ Configuration.new(
14
+ archive_menu: root_config(base_config).merge(section_config(sections))))
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :loader, :config_filename
20
+
21
+ def root_config(base_config)
22
+ { '.' => base_config.archive_menu }
23
+ end
24
+
25
+ def section_config(sections)
26
+ sections.reduce({}) {|config, section|
27
+ config_path = section.path_to_repository.join(config_filename)
28
+ archive_config = loader.load_key(config_path, 'archive_menu')
29
+ if archive_config
30
+ config.merge(section.desired_directory_name => archive_config)
31
+ else
32
+ config
33
+ end
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class ArchiveMenuChecker
5
+ MissingArchiveMenuPartialError = Class.new(RuntimeError)
6
+ ArchiveMenuNotDefinedError = Class.new(RuntimeError)
7
+ EmptyArchiveItemsError = Class.new(RuntimeError)
8
+
9
+ def initialize(file_system_accessor)
10
+ @file_system_accessor = file_system_accessor
11
+ end
12
+
13
+ def check(config)
14
+ partial_location = './master_middleman/source/archive_menus/_default.erb'
15
+ if config.has_key?("archive_menu") && config["archive_menu"].nil?
16
+ 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.'
17
+ elsif archive_items(config).include?(nil)
18
+ EmptyArchiveItemsError.new 'Did you forget to add a value to the archive_menu?'
19
+ elsif config.has_key?("archive_menu") && !file_system_accessor.file_exist?(partial_location)
20
+ MissingArchiveMenuPartialError.new "You must provide a template partial named at #{partial_location}"
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :file_system_accessor
27
+
28
+ def archive_items(config)
29
+ config.fetch('archive_menu', [])
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class DitaSectionChecker
5
+ DitamapLocationError = Class.new(RuntimeError)
6
+
7
+ def check(config)
8
+ dita_sections = config['dita_sections'].to_a
9
+
10
+ sum = 0
11
+ dita_sections.each do |section|
12
+ if section['ditamap_location']
13
+ sum += 1
14
+ end
15
+ end
16
+
17
+ if !dita_sections.empty? && (sum < 1)
18
+ DitamapLocationError.new "You must have at least one 'ditamap_location' key in dita_sections."
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class DuplicateSectionNameChecker
5
+ DuplicateSectionNameError = Class.new(RuntimeError)
6
+
7
+ def check(config)
8
+ if duplicate_section_names?(config)
9
+ DuplicateSectionNameError.new error_message
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def duplicate_section_names?(config)
16
+ sections = config['sections'].to_a + config['dita_sections'].to_a
17
+ directory_names = sections.map {|section| section['directory']}
18
+ directory_names.length != directory_names.uniq.length
19
+ end
20
+
21
+ def error_message
22
+ <<-ERROR
23
+ Duplicate repository names are not allowed.
24
+ ERROR
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class RepositoryNamePresenceChecker
5
+ MissingRepositoryNameError = Class.new(RuntimeError)
6
+
7
+ def check(config)
8
+ all_sections = config['sections'].to_a + config['dita_sections'].to_a
9
+ failures = all_sections.map do |section|
10
+ if !section['repository'] || !section['repository']['name']
11
+ true
12
+ end
13
+ end
14
+
15
+ if failures.compact.empty?
16
+ nil
17
+ else
18
+ MissingRepositoryNameError.new error_message
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def error_message
25
+ <<-ERROR
26
+ Cannot locate a specific section.
27
+ All sections must provide the section 'name' key under the 'repository' key:
28
+
29
+ sections:
30
+ - repository:
31
+ name: 'your-org/your-repo'
32
+ ERROR
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ require_relative '../configuration'
2
+
3
+ module Bookbinder
4
+ module Config
5
+ module Checkers
6
+ class RequiredKeysChecker
7
+ MissingRequiredKeyError = Class.new(RuntimeError)
8
+ SectionAbsenceError = Class.new(RuntimeError)
9
+
10
+ def check(config)
11
+ missing_keys = []
12
+
13
+ Config::Configuration::CONFIG_REQUIRED_KEYS.map do |required_key|
14
+ config_keys = config.keys
15
+ unless config_keys.include?(required_key)
16
+ missing_keys.push(required_key)
17
+ end
18
+ end
19
+
20
+ if missing_keys.length > 0
21
+ MissingRequiredKeyError.new("Your config.yml is missing required key(s). Required keys are #{missing_keys.join(", ")}.")
22
+ elsif !config['sections'] && !config['dita_sections']
23
+ SectionAbsenceError.new error_message
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def error_message
30
+ <<-ERROR
31
+ Cannot locate your sections.
32
+ Must specify at least one of 'sections' and/or 'dita_sections' in config.yml:
33
+
34
+ sections:
35
+ - repository:
36
+ name: 'your-org/your-repo'
37
+
38
+ dita_sections:
39
+ - repository:
40
+ name: 'dita-org/dita-repo'
41
+ ditamap_location: 'example.ditamap'
42
+ ERROR
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,99 @@
1
+ require_relative '../ingest/repo_identifier'
2
+ require_relative 'section_config'
3
+
4
+ module Bookbinder
5
+ module Config
6
+ class Configuration
7
+ class << self
8
+ def parse(input_config)
9
+ new(symbolize_keys(input_config).
10
+ merge(expand_repo_identifiers(input_config)).
11
+ merge(sections: combined_sections(input_config)))
12
+ end
13
+
14
+ private
15
+
16
+ def symbolize_keys(h)
17
+ h.reduce({}) {|acc, (k, v)| acc.merge(k.to_sym => v) }
18
+ end
19
+
20
+ def expand_repo_identifiers(input_config)
21
+ input_config.select {|k, _| k.match(/_repo$/)}.
22
+ reduce({}) {|h, (k, v)| h.merge(:"#{k}_url" => Ingest::RepoIdentifier.new(v))}
23
+ end
24
+
25
+ def combined_sections(input_config)
26
+ (regular_sections(input_config) + dita_sections(input_config)).
27
+ map { |section| Config::SectionConfig.new(section) }
28
+ end
29
+
30
+ def regular_sections(input_config)
31
+ input_config['sections'] || []
32
+ end
33
+
34
+ def dita_sections(input_config)
35
+ (input_config['dita_sections'] || []).map { |dita_section|
36
+ dita_section.merge(
37
+ 'preprocessor_config' => {
38
+ 'ditamap_location' => dita_section['ditamap_location'],
39
+ 'ditaval_location' => dita_section['ditaval_location']
40
+ },
41
+ 'subnav_template' => 'dita_subnav'
42
+ ).reject { |k, _|
43
+ %w(ditamap_location ditaval_location).include?(k)
44
+ }
45
+ }
46
+ end
47
+ end
48
+
49
+ def initialize(config)
50
+ @config = config
51
+ end
52
+
53
+ CONFIG_REQUIRED_KEYS = %w(book_repo public_host)
54
+ CONFIG_OPTIONAL_KEYS = %w(archive_menu book_repo_url cred_repo cred_repo_url layout_repo layout_repo_url sections)
55
+
56
+ CONFIG_REQUIRED_KEYS.each do |method_name|
57
+ define_method(method_name) do
58
+ config.fetch(method_name.to_sym)
59
+ end
60
+ end
61
+
62
+ CONFIG_OPTIONAL_KEYS.each do |method_name|
63
+ define_method(method_name) do
64
+ config[method_name.to_sym]
65
+ end
66
+ end
67
+
68
+ def template_variables
69
+ config.fetch(:template_variables, {})
70
+ end
71
+
72
+ def versions
73
+ config.fetch(:versions, [])
74
+ end
75
+
76
+ def merge(other_configuration)
77
+ Configuration.new(config.merge(other_configuration.instance_variable_get(:@config)))
78
+ end
79
+
80
+ def merge_sections(incoming_sections)
81
+ merge(Configuration.new(sections: sections + incoming_sections))
82
+ end
83
+
84
+ def has_option?(key)
85
+ !!config[key.to_sym]
86
+ end
87
+
88
+ def ==(o)
89
+ o.class == self.class && o.instance_variable_get(:@config) == @config
90
+ end
91
+
92
+ alias_method :eql?, :==
93
+
94
+ private
95
+
96
+ attr_reader :config
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,61 @@
1
+ require_relative 'aws_credentials'
2
+ require_relative 'cf_credentials'
3
+ require_relative 'configuration'
4
+ require_relative 'yaml_loader'
5
+
6
+ module Bookbinder
7
+ module Config
8
+ class Fetcher
9
+ def initialize(logger, configuration_validator, loader, credentials_provider)
10
+ @loader = loader
11
+ @logger = logger
12
+ @configuration_validator = configuration_validator
13
+ @credentials_provider = credentials_provider
14
+ end
15
+
16
+ def fetch_config
17
+ @config ||= validate(read_config_file)
18
+ end
19
+
20
+ def fetch_credentials(environment = 'null-environment')
21
+ @credentials ||= credentials_provider.credentials(fetch_config.cred_repo_url)
22
+ {
23
+ aws: Config::AwsCredentials.new(
24
+ @credentials.fetch('aws', {})
25
+ ),
26
+ cloud_foundry: Config::CfCredentials.new(
27
+ @credentials.fetch('cloud_foundry', {}),
28
+ environment
29
+ )
30
+ }
31
+ end
32
+
33
+ def set_config_file_path config_file_path
34
+ @config_file_path = File.expand_path config_file_path
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader(:loader, :logger, :configuration_validator, :config, :config_file_path,
40
+ :credentials_provider)
41
+
42
+ def read_config_file
43
+ loader.load(config_file_path)
44
+
45
+ rescue FileNotFoundError => e
46
+ raise "The configuration file specified does not exist. Please create a config #{e} file at #{config_file_path} and try again."
47
+ rescue InvalidSyntaxError => e
48
+ raise "There is a syntax error in your config file: \n #{e}"
49
+ end
50
+
51
+ def validate(config_hash)
52
+ raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
53
+
54
+ errors = configuration_validator.exceptions(config_hash)
55
+ raise errors.first if errors.any?
56
+
57
+ Configuration.parse(config_hash)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,22 @@
1
+ require 'yaml'
2
+ require 'ansi/code'
3
+
4
+ module Bookbinder
5
+ module Config
6
+ class RemoteYamlCredentialProvider
7
+ def initialize(logger, version_control_system)
8
+ @logger = logger
9
+ @version_control_system = version_control_system
10
+ end
11
+
12
+ def credentials(repo_url)
13
+ logger.log "Processing #{ANSI.cyan{repo_url}}"
14
+ YAML.load(version_control_system.read_file("credentials.yml", from_repo: repo_url))
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :logger, :version_control_system
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'checkers/duplicate_section_name_checker'
2
+ require_relative 'checkers/archive_menu_checker'
3
+ require_relative 'checkers/required_keys_checker'
4
+ require_relative 'checkers/repository_name_presence_checker'
5
+ require_relative 'checkers/dita_section_checker'
6
+
7
+ module Bookbinder
8
+ module Config
9
+ class Validator
10
+ def initialize(logger, file_system_accessor)
11
+ @logger = logger
12
+ @file_system_accessor = file_system_accessor
13
+ end
14
+
15
+ def exceptions(config_hash)
16
+ exceptions = [
17
+ Checkers::RequiredKeysChecker.new,
18
+ Checkers::DuplicateSectionNameChecker.new,
19
+ Checkers::RepositoryNamePresenceChecker.new,
20
+ Checkers::DitaSectionChecker.new,
21
+ Checkers::ArchiveMenuChecker.new(@file_system_accessor)
22
+ ].map do |checker|
23
+ checker.check(config_hash)
24
+ end
25
+
26
+ exceptions.compact
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+
3
+ module Bookbinder
4
+ module Config
5
+ FileNotFoundError = Class.new(RuntimeError)
6
+ InvalidSyntaxError = Class.new(RuntimeError)
7
+
8
+ class YAMLLoader
9
+ def load(path)
10
+ if File.exist?(path)
11
+ config(path)
12
+ else
13
+ raise FileNotFoundError.new, "YAML"
14
+ end
15
+ rescue Psych::SyntaxError => e
16
+ raise InvalidSyntaxError.new e
17
+ end
18
+
19
+ def load_key(path, key)
20
+ if File.exist?(path)
21
+ config(path)[key]
22
+ end
23
+ rescue Psych::SyntaxError => e
24
+ raise InvalidSyntaxError.new e
25
+ end
26
+
27
+ private
28
+
29
+ def config(path)
30
+ YAML.load_file(path) || {}
31
+ end
32
+ end
33
+ end
34
+ end