bookbindery 3.1.0 → 3.1.2
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.
- checksums.yaml +4 -4
- data/install_bin/bookbinder +1 -1
- data/lib/bookbinder/cf_command_runner.rb +1 -1
- data/lib/bookbinder/cli.rb +5 -5
- data/lib/bookbinder/code_example_reader.rb +57 -19
- data/lib/bookbinder/colorizer.rb +5 -2
- data/lib/bookbinder/commands/bind.rb +32 -42
- data/lib/bookbinder/commands/bind/bind_options.rb +6 -4
- data/lib/bookbinder/commands/bind/directory_preparer.rb +8 -13
- data/lib/bookbinder/{repositories/command_repository.rb → commands/collection.rb} +19 -37
- data/lib/bookbinder/config/archive_menu_configuration.rb +38 -0
- data/lib/bookbinder/config/checkers/archive_menu_checker.rb +35 -0
- data/lib/bookbinder/config/checkers/dita_section_checker.rb +24 -0
- data/lib/bookbinder/config/checkers/duplicate_section_name_checker.rb +29 -0
- data/lib/bookbinder/config/checkers/repository_name_presence_checker.rb +37 -0
- data/lib/bookbinder/config/checkers/required_keys_checker.rb +47 -0
- data/lib/bookbinder/config/configuration.rb +99 -0
- data/lib/bookbinder/config/fetcher.rb +61 -0
- data/lib/bookbinder/config/remote_yaml_credential_provider.rb +22 -0
- data/lib/bookbinder/config/validator.rb +30 -0
- data/lib/bookbinder/config/yaml_loader.rb +34 -0
- data/lib/bookbinder/ingest/git_cloner.rb +0 -1
- data/lib/bookbinder/ingest/local_filesystem_cloner.rb +22 -17
- data/lib/bookbinder/ingest/missing_working_copy.rb +21 -0
- data/lib/bookbinder/{repositories → ingest}/section_repository.rb +3 -4
- data/lib/bookbinder/{repositories → ingest}/section_repository_factory.rb +1 -1
- data/lib/bookbinder/ingest/working_copy.rb +12 -16
- data/lib/bookbinder/preprocessing/copy_to_site_gen_dir.rb +2 -3
- data/lib/bookbinder/preprocessing/dita_preprocessor.rb +5 -5
- data/lib/bookbinder/preprocessing/preprocessor.rb +1 -1
- data/lib/bookbinder/sheller.rb +3 -0
- data/lib/bookbinder/streams/colorized_stream.rb +1 -1
- data/lib/bookbinder/values/cf_routes.rb +17 -6
- data/lib/bookbinder/values/output_locations.rb +1 -12
- data/lib/bookbinder/values/section.rb +2 -7
- data/master_middleman/bookbinder_helpers.rb +23 -18
- data/master_middleman/quicklinks_renderer.rb +1 -0
- data/template_app/lib/rack_static.rb +2 -0
- data/template_app/lib/vienna_application.rb +2 -0
- metadata +51 -52
- data/lib/bookbinder.rb +0 -44
- data/lib/bookbinder/archive_menu_configuration.rb +0 -34
- data/lib/bookbinder/configuration.rb +0 -97
- data/lib/bookbinder/configuration_fetcher.rb +0 -59
- data/lib/bookbinder/configuration_validator.rb +0 -28
- data/lib/bookbinder/remote_yaml_credential_provider.rb +0 -20
- data/lib/bookbinder/shell_out.rb +0 -20
- data/lib/bookbinder/validation_checkers/archive_menu_checker.rb +0 -31
- data/lib/bookbinder/validation_checkers/dita_section_checker.rb +0 -20
- data/lib/bookbinder/validation_checkers/duplicate_section_name_checker.rb +0 -25
- data/lib/bookbinder/validation_checkers/repository_name_presence_checker.rb +0 -33
- data/lib/bookbinder/validation_checkers/required_keys_checker.rb +0 -43
- 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
|