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