bookbindery 1.0.0
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 +7 -0
- data/bin/bookbinder +6 -0
- data/lib/bookbinder.rb +59 -0
- data/lib/bookbinder/app_fetcher.rb +40 -0
- data/lib/bookbinder/archive.rb +95 -0
- data/lib/bookbinder/artifact_namer.rb +22 -0
- data/lib/bookbinder/blue_green_app.rb +27 -0
- data/lib/bookbinder/book.rb +54 -0
- data/lib/bookbinder/bookbinder_logger.rb +33 -0
- data/lib/bookbinder/cf_command_runner.rb +114 -0
- data/lib/bookbinder/cf_routes.rb +19 -0
- data/lib/bookbinder/cli.rb +68 -0
- data/lib/bookbinder/cli_error.rb +6 -0
- data/lib/bookbinder/code_example.rb +40 -0
- data/lib/bookbinder/command_runner.rb +32 -0
- data/lib/bookbinder/command_validator.rb +24 -0
- data/lib/bookbinder/commands/bookbinder_command.rb +18 -0
- data/lib/bookbinder/commands/build_and_push_tarball.rb +31 -0
- data/lib/bookbinder/commands/generate_pdf.rb +140 -0
- data/lib/bookbinder/commands/help.rb +31 -0
- data/lib/bookbinder/commands/naming.rb +9 -0
- data/lib/bookbinder/commands/publish.rb +138 -0
- data/lib/bookbinder/commands/push_local_to_staging.rb +35 -0
- data/lib/bookbinder/commands/push_to_prod.rb +35 -0
- data/lib/bookbinder/commands/run_publish_ci.rb +42 -0
- data/lib/bookbinder/commands/tag.rb +31 -0
- data/lib/bookbinder/commands/update_local_doc_repos.rb +27 -0
- data/lib/bookbinder/commands/version.rb +25 -0
- data/lib/bookbinder/configuration.rb +163 -0
- data/lib/bookbinder/configuration_fetcher.rb +55 -0
- data/lib/bookbinder/configuration_validator.rb +162 -0
- data/lib/bookbinder/css_link_checker.rb +64 -0
- data/lib/bookbinder/directory_helpers.rb +15 -0
- data/lib/bookbinder/distributor.rb +69 -0
- data/lib/bookbinder/git_client.rb +63 -0
- data/lib/bookbinder/git_hub_repository.rb +151 -0
- data/lib/bookbinder/local_file_system_accessor.rb +9 -0
- data/lib/bookbinder/middleman_runner.rb +86 -0
- data/lib/bookbinder/pdf_generator.rb +73 -0
- data/lib/bookbinder/publisher.rb +125 -0
- data/lib/bookbinder/pusher.rb +34 -0
- data/lib/bookbinder/remote_yaml_credential_provider.rb +21 -0
- data/lib/bookbinder/section.rb +78 -0
- data/lib/bookbinder/server_director.rb +53 -0
- data/lib/bookbinder/shell_out.rb +19 -0
- data/lib/bookbinder/sieve.rb +62 -0
- data/lib/bookbinder/sitemap_generator.rb +19 -0
- data/lib/bookbinder/spider.rb +91 -0
- data/lib/bookbinder/stabilimentum.rb +59 -0
- data/lib/bookbinder/usage_messenger.rb +33 -0
- data/lib/bookbinder/yaml_loader.rb +22 -0
- data/master_middleman/bookbinder_helpers.rb +133 -0
- data/master_middleman/config.rb +23 -0
- data/master_middleman/quicklinks_renderer.rb +78 -0
- data/master_middleman/submodule_aware_assets.rb +45 -0
- data/template_app/Gemfile +7 -0
- data/template_app/Gemfile.lock +20 -0
- data/template_app/app.rb +3 -0
- data/template_app/config.ru +9 -0
- data/template_app/lib/rack_static.rb +19 -0
- data/template_app/lib/vienna_application.rb +26 -0
- metadata +462 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative '../distributor'
|
2
|
+
require_relative 'bookbinder_command'
|
3
|
+
require_relative 'naming'
|
4
|
+
|
5
|
+
module Bookbinder
|
6
|
+
module Commands
|
7
|
+
class PushLocalToStaging < BookbinderCommand
|
8
|
+
extend Commands::Naming
|
9
|
+
|
10
|
+
def self.usage
|
11
|
+
"push_local_to_staging \t \t \t Push the contents of final_app to the staging host specified in credentials.yml"
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(_)
|
15
|
+
Distributor.build(@logger, options).distribute
|
16
|
+
0
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def options
|
22
|
+
{
|
23
|
+
app_dir: './final_app',
|
24
|
+
build_number: ENV['BUILD_NUMBER'],
|
25
|
+
|
26
|
+
aws_credentials: config.aws_credentials,
|
27
|
+
cf_credentials: config.cf_staging_credentials,
|
28
|
+
|
29
|
+
book_repo: config.book_repo,
|
30
|
+
production: false
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative '../distributor'
|
2
|
+
require_relative 'bookbinder_command'
|
3
|
+
require_relative 'naming'
|
4
|
+
|
5
|
+
module Bookbinder
|
6
|
+
module Commands
|
7
|
+
class PushToProd < BookbinderCommand
|
8
|
+
extend Commands::Naming
|
9
|
+
|
10
|
+
def self.usage
|
11
|
+
"push_to_prod [build_#] \t \t \t Push latest or <build_#> from your S3 bucket to the production host specified in credentials.yml"
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(arguments)
|
15
|
+
Distributor.build(@logger, options(arguments)).distribute
|
16
|
+
0
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def options(arguments)
|
22
|
+
{
|
23
|
+
app_dir: Dir.mktmpdir,
|
24
|
+
build_number: arguments[0],
|
25
|
+
|
26
|
+
aws_credentials: config.aws_credentials,
|
27
|
+
cf_credentials: config.cf_production_credentials,
|
28
|
+
|
29
|
+
book_repo: config.book_repo,
|
30
|
+
production: true
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'bookbinder_command'
|
2
|
+
require_relative 'naming'
|
3
|
+
require_relative 'publish'
|
4
|
+
require_relative 'push_local_to_staging'
|
5
|
+
require_relative 'build_and_push_tarball'
|
6
|
+
|
7
|
+
module Bookbinder
|
8
|
+
module Commands
|
9
|
+
class RunPublishCI < BookbinderCommand
|
10
|
+
extend Commands::Naming
|
11
|
+
|
12
|
+
def self.usage
|
13
|
+
"run_publish_ci \t \t \t \t Run publish, push_local_to_staging, and build_and_push_tarball for CI purposes"
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(cli_args)
|
17
|
+
check_params
|
18
|
+
all_successfully_ran = publish(cli_args) == 0 && push_to_staging == 0 && push_tarball == 0
|
19
|
+
all_successfully_ran ? 0 : 1
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def check_params
|
25
|
+
raise BuildAndPushTarball::MissingBuildNumber unless ENV['BUILD_NUMBER']
|
26
|
+
config.book_repo
|
27
|
+
end
|
28
|
+
|
29
|
+
def publish(cli_args)
|
30
|
+
Publish.new(@logger, @configuration_fetcher).run(['github'] + cli_args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def push_to_staging
|
34
|
+
PushLocalToStaging.new(@logger, @configuration_fetcher).run []
|
35
|
+
end
|
36
|
+
|
37
|
+
def push_tarball
|
38
|
+
BuildAndPushTarball.new(@logger, @configuration_fetcher).run []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative '../book'
|
2
|
+
require_relative '../bookbinder_logger'
|
3
|
+
require_relative '../cli_error'
|
4
|
+
|
5
|
+
require_relative 'bookbinder_command'
|
6
|
+
require_relative 'naming'
|
7
|
+
|
8
|
+
module Bookbinder
|
9
|
+
module Commands
|
10
|
+
class Tag < BookbinderCommand
|
11
|
+
extend Commands::Naming
|
12
|
+
|
13
|
+
def self.usage
|
14
|
+
"tag <git tag> \t \t \t \t Apply the specified <git tag> to your book and all sections of your book"
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(params)
|
18
|
+
tag = params.first
|
19
|
+
raise CliError::InvalidArguments unless tag
|
20
|
+
|
21
|
+
book = Book.new(logger: @logger, full_name: config.book_repo, sections: config.sections)
|
22
|
+
|
23
|
+
book.tag_self_and_sections_with tag
|
24
|
+
|
25
|
+
@logger.log 'Success!'.green
|
26
|
+
@logger.log " #{book.full_name.yellow} and its sections were tagged with #{tag.blue}"
|
27
|
+
0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'bookbinder_command'
|
2
|
+
require_relative 'naming'
|
3
|
+
|
4
|
+
module Bookbinder
|
5
|
+
module Commands
|
6
|
+
class UpdateLocalDocRepos < BookbinderCommand
|
7
|
+
extend Commands::Naming
|
8
|
+
|
9
|
+
def self.usage
|
10
|
+
"update_local_doc_repos \t \t \t Run `git pull` on all sections that exist at the same directory level as your book directory"
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(_)
|
14
|
+
config.sections.map { |conf| repo_for(conf) }.each(&:update_local_copy)
|
15
|
+
0
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def repo_for(section_config)
|
21
|
+
local_repo_dir = File.absolute_path('../')
|
22
|
+
GitHubRepository.new(logger: @logger, full_name: section_config['repository']['name'],
|
23
|
+
local_repo_dir: local_repo_dir)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'bookbinder_command'
|
2
|
+
|
3
|
+
module Bookbinder
|
4
|
+
module Commands
|
5
|
+
class Version < BookbinderCommand
|
6
|
+
def self.to_s
|
7
|
+
'version'
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.command_name
|
11
|
+
'--version'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.usage
|
15
|
+
"--version \t \t \t \t Print the version of bookbinder"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(*)
|
19
|
+
@logger.log "bookbinder #{Gem::Specification::load(File.join GEM_ROOT, "bookbinder.gemspec").version}"
|
20
|
+
0
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require_relative 'remote_yaml_credential_provider'
|
2
|
+
require_relative 'git_hub_repository'
|
3
|
+
|
4
|
+
module Bookbinder
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
CURRENT_SCHEMA_VERSION = '1.0.0'
|
8
|
+
STARTING_SCHEMA_VERSION = '1.0.0'
|
9
|
+
|
10
|
+
class CredentialKeyError < StandardError;
|
11
|
+
end
|
12
|
+
|
13
|
+
class ConfigSchemaUnsupportedError < StandardError;
|
14
|
+
end
|
15
|
+
|
16
|
+
class AwsCredentials
|
17
|
+
REQUIRED_KEYS = %w(access_key secret_key green_builds_bucket).freeze
|
18
|
+
|
19
|
+
def initialize(cred_hash)
|
20
|
+
@creds = cred_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
REQUIRED_KEYS.each do |method_name|
|
24
|
+
define_method(method_name) do
|
25
|
+
begin
|
26
|
+
creds.fetch(method_name)
|
27
|
+
rescue KeyError => e
|
28
|
+
raise CredentialKeyError, e
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :creds
|
36
|
+
end
|
37
|
+
|
38
|
+
class CfCredentials
|
39
|
+
REQUIRED_KEYS = %w(api_endpoint organization app_name).freeze
|
40
|
+
OPTIONAL_KEYS = %w(username password production_space production_host staging_space staging_host).freeze
|
41
|
+
|
42
|
+
def initialize(cred_hash, is_production)
|
43
|
+
@creds = cred_hash
|
44
|
+
@is_production = is_production
|
45
|
+
end
|
46
|
+
|
47
|
+
REQUIRED_KEYS.each do |method_name|
|
48
|
+
define_method(method_name) do
|
49
|
+
fetch(method_name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
OPTIONAL_KEYS.each do |method_name|
|
54
|
+
define_method(method_name) do
|
55
|
+
creds.fetch(method_name, nil)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def routes
|
60
|
+
key = is_production ? 'production_host' : 'staging_host'
|
61
|
+
fetch(key) if correctly_formatted_domain_and_routes?(key)
|
62
|
+
end
|
63
|
+
|
64
|
+
def flat_routes
|
65
|
+
routes.reduce([]) do |all_routes, domain_apps|
|
66
|
+
domain, apps = domain_apps
|
67
|
+
all_routes + apps.map { |app| [domain, app] }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def space
|
72
|
+
key = is_production ? 'production_space' : 'staging_space'
|
73
|
+
fetch(key)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
attr_reader :creds, :is_production
|
79
|
+
|
80
|
+
def fetch(key)
|
81
|
+
creds.fetch(key)
|
82
|
+
rescue KeyError => e
|
83
|
+
raise CredentialKeyError, e
|
84
|
+
end
|
85
|
+
|
86
|
+
def correctly_formatted_domain_and_routes?(deploy_environment)
|
87
|
+
routes_hash = fetch(deploy_environment)
|
88
|
+
domains = routes_hash.keys
|
89
|
+
domains.each { |domain| correctly_formatted_domain?(domain, routes_hash) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def correctly_formatted_domain?(domain, routes_hash)
|
93
|
+
raise 'Each domain in credentials must be a single string.' unless domain.is_a? String
|
94
|
+
raise "Domain #{domain} in credentials must contain a web extension, e.g. '.com'." unless domain.include?('.')
|
95
|
+
raise "Did you mean to add a list of hosts for domain #{domain}? Check your credentials.yml." unless routes_hash[domain]
|
96
|
+
raise "Hosts in credentials must be nested as an array under the desired domain #{domain}." unless routes_hash[domain].is_a? Array
|
97
|
+
raise "Did you mean to provide a hostname for the domain #{domain}? Check your credentials.yml." if routes_hash[domain].any?(&:nil?)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
attr_reader :schema_version, :schema_major_version, :schema_minor_version, :schema_patch_version
|
102
|
+
|
103
|
+
def initialize(logger, config_hash)
|
104
|
+
@logger = logger
|
105
|
+
@config = config_hash
|
106
|
+
end
|
107
|
+
|
108
|
+
CONFIG_REQUIRED_KEYS = %w(book_repo layout_repo cred_repo sections public_host pdf pdf_index versions)
|
109
|
+
CONFIG_OPTIONAL_KEYS = %w(archive_menu)
|
110
|
+
|
111
|
+
CONFIG_REQUIRED_KEYS.each do |method_name|
|
112
|
+
define_method(method_name) do
|
113
|
+
config.fetch(method_name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
CONFIG_OPTIONAL_KEYS.each do |method_name|
|
118
|
+
define_method(method_name) do
|
119
|
+
config[method_name]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def has_option?(key)
|
124
|
+
@config.has_key?(key)
|
125
|
+
end
|
126
|
+
|
127
|
+
def template_variables
|
128
|
+
config.fetch('template_variables', {})
|
129
|
+
end
|
130
|
+
|
131
|
+
def aws_credentials
|
132
|
+
@aws_creds ||= AwsCredentials.new(credentials.fetch('aws'))
|
133
|
+
end
|
134
|
+
|
135
|
+
def cf_staging_credentials
|
136
|
+
@cf_staging_creds ||= CfCredentials.new(credentials.fetch('cloud_foundry'), false)
|
137
|
+
end
|
138
|
+
|
139
|
+
def cf_production_credentials
|
140
|
+
@cf_prod_creds ||= CfCredentials.new(credentials.fetch('cloud_foundry'), true)
|
141
|
+
end
|
142
|
+
|
143
|
+
def ==(o)
|
144
|
+
(o.class == self.class) && (o.config == self.config)
|
145
|
+
end
|
146
|
+
|
147
|
+
alias_method :eql?, :==
|
148
|
+
|
149
|
+
protected
|
150
|
+
|
151
|
+
attr_reader :config
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def credentials
|
156
|
+
@credentials ||= RemoteYamlCredentialProvider.new(@logger, credentials_repository).credentials
|
157
|
+
end
|
158
|
+
|
159
|
+
def credentials_repository
|
160
|
+
@credentials_repository ||= GitHubRepository.new(logger: @logger, full_name: cred_repo)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'yaml_loader'
|
2
|
+
|
3
|
+
module Bookbinder
|
4
|
+
|
5
|
+
class ConfigurationFetcher
|
6
|
+
attr_reader :logger,
|
7
|
+
:configuration_validator,
|
8
|
+
:config,
|
9
|
+
:config_file_path
|
10
|
+
|
11
|
+
def initialize(logger, configuration_validator, loader)
|
12
|
+
@loader = loader
|
13
|
+
@logger = logger
|
14
|
+
@configuration_validator = configuration_validator
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch_config
|
18
|
+
@config ||= validate(read_config_file)
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_config_file_path config_file_path
|
22
|
+
@config_file_path = config_file_path
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :loader
|
29
|
+
|
30
|
+
def read_config_file
|
31
|
+
|
32
|
+
begin
|
33
|
+
config_hash = loader.load(config_file_path)
|
34
|
+
rescue FileNotFoundError => e
|
35
|
+
raise "The configuration file specified does not exist. Please create a config #{e} file at #{config_file_path} and try again."
|
36
|
+
rescue InvalidSyntaxError => e
|
37
|
+
raise "There is a syntax error in your config file: \n #{e}"
|
38
|
+
end
|
39
|
+
|
40
|
+
if config_hash
|
41
|
+
if File.exists?('./pdf_index.yml')
|
42
|
+
config_hash['pdf_index'] = loader.load(config_file_path)
|
43
|
+
else
|
44
|
+
config_hash['pdf_index'] = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
config_hash
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate(config_hash)
|
52
|
+
Configuration.new(logger, config_hash) if configuration_validator.valid?(config_hash, Configuration::CURRENT_SCHEMA_VERSION, Configuration::STARTING_SCHEMA_VERSION)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module Bookbinder
|
2
|
+
|
3
|
+
class DuplicateSectionNameChecker
|
4
|
+
def check(config)
|
5
|
+
if duplicate_section_names?(config)
|
6
|
+
ConfigurationValidator::DuplicateSectionNameError
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def duplicate_section_names?(config)
|
13
|
+
directory_names = config['sections'].map {|section| section['directory']}
|
14
|
+
directory_names.length != directory_names.uniq.length
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class ArchiveMenuChecker
|
20
|
+
def initialize(file_system_accessor)
|
21
|
+
@file_system_accessor = file_system_accessor
|
22
|
+
end
|
23
|
+
|
24
|
+
def check(config)
|
25
|
+
partial_location = './master_middleman/source/archive_menus/_default.erb'
|
26
|
+
if config.has_key?("archive_menu") && config["archive_menu"].nil?
|
27
|
+
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.'
|
28
|
+
elsif archive_items(config).include?(nil)
|
29
|
+
ConfigurationValidator::EmptyArchiveItemsError.new 'Did you forget to add a value to the archive_menu?'
|
30
|
+
elsif config.has_key?("archive_menu") && !@file_system_accessor.file_exist?(partial_location)
|
31
|
+
ConfigurationValidator::MissingArchiveMenuPartialError.new "You must provide a template partial named at #{partial_location}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def archive_items(config)
|
38
|
+
config.fetch('archive_menu', [])
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
Version = Struct.new(:major, :minor, :patch) do
|
44
|
+
class << self
|
45
|
+
def parse(raw_version)
|
46
|
+
if raw_version
|
47
|
+
new(*raw_version.split('.'))
|
48
|
+
else
|
49
|
+
new(nil, nil, nil)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def valid?
|
55
|
+
[major, minor, patch].all?(&:present?)
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
[major, minor, patch].compact.join('.')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class VersionCheckerMessages
|
64
|
+
def initialize(user_schema_version, bookbinder_schema_version)
|
65
|
+
@user_schema_version = user_schema_version
|
66
|
+
@bookbinder_schema_version = bookbinder_schema_version
|
67
|
+
end
|
68
|
+
|
69
|
+
def schema_now_required_message
|
70
|
+
"[ERROR] Bookbinder now requires a certain schema. Please see README " +
|
71
|
+
"and provide a schema version."
|
72
|
+
end
|
73
|
+
|
74
|
+
def incompatible_schema_message
|
75
|
+
"[ERROR] Your config.yml format, schema version #{user_schema_version}, " +
|
76
|
+
"is older than this version of Bookbinder can support. Please update " +
|
77
|
+
"your config.yml keys and format to version #{bookbinder_schema_version} " +
|
78
|
+
"and try again."
|
79
|
+
end
|
80
|
+
|
81
|
+
def unrecognized_schema_version_message
|
82
|
+
"[ERROR] The config schema version #{user_schema_version} is " +
|
83
|
+
"unrecognized by this version of Bookbinder. The latest schema version " +
|
84
|
+
"is #{bookbinder_schema_version}."
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
attr_reader :user_schema_version, :bookbinder_schema_version
|
90
|
+
end
|
91
|
+
|
92
|
+
class ConfigVersionChecker
|
93
|
+
def initialize(bookbinder_schema_version, starting_schema_version, messages, logger)
|
94
|
+
@bookbinder_schema_version = bookbinder_schema_version
|
95
|
+
@starting_schema_version = starting_schema_version
|
96
|
+
@messages = messages
|
97
|
+
@logger = logger
|
98
|
+
end
|
99
|
+
|
100
|
+
def check(config)
|
101
|
+
user_schema_version = Version.parse(config['schema_version'])
|
102
|
+
if user_schema_version.valid?
|
103
|
+
if user_schema_version.major > bookbinder_schema_version.major
|
104
|
+
raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
|
105
|
+
elsif user_schema_version.minor > bookbinder_schema_version.minor
|
106
|
+
raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
|
107
|
+
elsif user_schema_version.patch > bookbinder_schema_version.patch
|
108
|
+
raise Configuration::ConfigSchemaUnsupportedError.new messages.unrecognized_schema_version_message
|
109
|
+
elsif user_schema_version.major < bookbinder_schema_version.major
|
110
|
+
raise Configuration::ConfigSchemaUnsupportedError.new messages.incompatible_schema_message
|
111
|
+
elsif user_schema_version.minor < bookbinder_schema_version.minor
|
112
|
+
@logger.warn nonbreaking_schema_message_for("minor")
|
113
|
+
elsif user_schema_version.patch < bookbinder_schema_version.patch
|
114
|
+
@logger.warn nonbreaking_schema_message_for("patch")
|
115
|
+
end
|
116
|
+
elsif bookbinder_schema_version != starting_schema_version
|
117
|
+
raise Configuration::ConfigSchemaUnsupportedError.new messages.schema_now_required_message
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
attr_reader :bookbinder_schema_version, :starting_schema_version, :messages
|
124
|
+
|
125
|
+
def nonbreaking_schema_message_for(version_level)
|
126
|
+
"[WARNING] Your schema is valid, but there exists a new #{version_level} version. Consider updating your config.yml."
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class ConfigurationValidator
|
131
|
+
DuplicateSectionNameError = Class.new(RuntimeError)
|
132
|
+
MissingArchiveMenuPartialError = Class.new(RuntimeError)
|
133
|
+
EmptyArchiveItemsError = Class.new(RuntimeError)
|
134
|
+
ArchiveMenuNotDefinedError = Class.new(RuntimeError)
|
135
|
+
|
136
|
+
def initialize(logger, file_system_accessor)
|
137
|
+
@logger = logger
|
138
|
+
@file_system_accessor = file_system_accessor
|
139
|
+
end
|
140
|
+
|
141
|
+
def valid?(config_hash, bookbinder_schema_version, starting_schema_version)
|
142
|
+
raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
|
143
|
+
|
144
|
+
user_config_schema_version = config_hash['schema_version']
|
145
|
+
exceptions = [
|
146
|
+
ConfigVersionChecker.new(Version.parse(bookbinder_schema_version),
|
147
|
+
Version.parse(starting_schema_version),
|
148
|
+
VersionCheckerMessages.new(Version.parse(user_config_schema_version),
|
149
|
+
bookbinder_schema_version),
|
150
|
+
@logger),
|
151
|
+
DuplicateSectionNameChecker.new,
|
152
|
+
ArchiveMenuChecker.new(@file_system_accessor)
|
153
|
+
].map do |checker|
|
154
|
+
checker.check(config_hash)
|
155
|
+
end
|
156
|
+
exception = exceptions.compact.first
|
157
|
+
raise exception if exception
|
158
|
+
|
159
|
+
true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|