bookbindery 3.1.2 → 4.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 +4 -4
- data/install_bin/bookbinder +2 -2
- data/lib/bookbinder/command_runner.rb +1 -1
- data/lib/bookbinder/command_validator.rb +1 -1
- data/lib/bookbinder/commands/bind.rb +12 -11
- data/lib/bookbinder/commands/bind/bind_options.rb +2 -4
- data/lib/bookbinder/commands/collection.rb +14 -4
- data/lib/bookbinder/commands/update_local_doc_repos.rb +18 -9
- data/lib/bookbinder/config/checkers/archive_menu_checker.rb +2 -8
- data/lib/bookbinder/config/checkers/dita_section_checker.rb +17 -10
- data/lib/bookbinder/config/checkers/duplicate_section_name_checker.rb +6 -2
- data/lib/bookbinder/config/checkers/repository_name_presence_checker.rb +3 -6
- data/lib/bookbinder/config/checkers/required_keys_checker.rb +4 -24
- data/lib/bookbinder/config/configuration.rb +4 -0
- data/lib/bookbinder/config/fetcher.rb +6 -7
- data/lib/bookbinder/config/section_config.rb +2 -2
- data/lib/bookbinder/config/validator.rb +3 -4
- data/lib/bookbinder/ingest/git_accessor.rb +84 -0
- data/lib/bookbinder/ingest/update_failure.rb +15 -0
- data/lib/bookbinder/ingest/update_success.rb +12 -0
- data/lib/bookbinder/post_production/sitemap_writer.rb +9 -4
- data/lib/bookbinder/server_director.rb +1 -2
- data/lib/bookbinder/spider.rb +26 -26
- data/lib/bookbinder/streams/colorized_stream.rb +4 -0
- data/master_middleman/bookbinder_helpers.rb +4 -8
- metadata +5 -3
- data/lib/bookbinder/git_accessor.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4186b0031cc1db33f20038d884a71624f845d175
|
4
|
+
data.tar.gz: 439d522cdaba03d005039b45e44b9b3917dab82d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 384fc6c0bf881e81cc2fd34040af92eae6b83150cbdd14b441d72d84355d2a8ce4dcb0044a892de797eec4f20b3660739fbb99529a0a7b0d57a8295fa827a0a6
|
7
|
+
data.tar.gz: 3310d764be0a0d8063e9142ccff2c55046f4378b255a8e5fe63ceb8919a5eb5ddbf10cf8d24584810f5b78678365654a931a2fc66df3f11b9649f54255fbacaf
|
data/install_bin/bookbinder
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require_relative '../lib/bookbinder/cli'
|
4
|
-
require_relative '../lib/bookbinder/git_accessor'
|
4
|
+
require_relative '../lib/bookbinder/ingest/git_accessor'
|
5
5
|
|
6
|
-
return_code = Bookbinder::Cli.new(Bookbinder::GitAccessor.new).run ARGV
|
6
|
+
return_code = Bookbinder::Cli.new(Bookbinder::Ingest::GitAccessor.new).run ARGV
|
7
7
|
exit return_code.to_i
|
@@ -11,7 +11,7 @@ module Bookbinder
|
|
11
11
|
command = commands.detect { |known_command| known_command.command_for?(command_name) }
|
12
12
|
command.run(command_arguments)
|
13
13
|
rescue CliError::InvalidArguments
|
14
|
-
logger.log command.usage
|
14
|
+
logger.log ["bookbinder #{command.usage[0]}", command.usage[1]]
|
15
15
|
1
|
16
16
|
end
|
17
17
|
|
@@ -20,7 +20,7 @@ module Bookbinder
|
|
20
20
|
elsif candidate.deprecated_match
|
21
21
|
UserMessage.new(
|
22
22
|
"Use of #{command_name} is deprecated. " +
|
23
|
-
"The preferred usage is below: \
|
23
|
+
"The preferred usage is below: \nbookbinder #{candidate.deprecated_match.usage[0]}",
|
24
24
|
EscalationType.warn
|
25
25
|
)
|
26
26
|
else
|
@@ -71,10 +71,12 @@ module Bookbinder
|
|
71
71
|
ref_override: bind_options.ref_override
|
72
72
|
)
|
73
73
|
|
74
|
-
preprocessor.preprocess(
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
preprocessor.preprocess(
|
75
|
+
sections,
|
76
|
+
output_locations,
|
77
|
+
options: options,
|
78
|
+
output_streams: bind_options.streams
|
79
|
+
)
|
78
80
|
|
79
81
|
success = publish(
|
80
82
|
sections.map(&:subnav).reduce({}, :merge),
|
@@ -118,19 +120,18 @@ module Bookbinder
|
|
118
120
|
subnavs)
|
119
121
|
file_system_accessor.copy output_locations.build_dir, output_locations.public_dir
|
120
122
|
|
121
|
-
|
122
|
-
result =
|
123
|
+
raise "Your public host must be a single String." unless host_for_sitemap.is_a?(String)
|
124
|
+
result = sitemap_writer.write(
|
125
|
+
host_for_sitemap,
|
126
|
+
streams,
|
127
|
+
publish_config.broken_link_exclusions
|
128
|
+
)
|
123
129
|
|
124
130
|
streams[:success].puts "Bookbinder bound your book into #{output_locations.final_app_dir}"
|
125
131
|
|
126
132
|
!result.has_broken_links?
|
127
133
|
end
|
128
134
|
|
129
|
-
def generate_sitemap(host_for_sitemap)
|
130
|
-
raise "Your public host must be a single String." unless host_for_sitemap.is_a?(String)
|
131
|
-
sitemap_writer.write(host_for_sitemap)
|
132
|
-
end
|
133
|
-
|
134
135
|
def generate_local_repo_dir(context_dir, bind_source)
|
135
136
|
File.expand_path('..', context_dir) if bind_source == 'local'
|
136
137
|
end
|
@@ -19,11 +19,9 @@ module Bookbinder
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def streams
|
22
|
-
|
22
|
+
base_streams.merge(
|
23
23
|
out: opts.include?('--verbose') ? base_streams[:out] : Sheller::DevNull.new,
|
24
|
-
|
25
|
-
err: Streams::ColorizedStream.new(Colorizer::Colors.red, base_streams[:err]),
|
26
|
-
}
|
24
|
+
)
|
27
25
|
end
|
28
26
|
|
29
27
|
private
|
@@ -65,7 +65,12 @@ module Bookbinder
|
|
65
65
|
Commands::PushToProd.new(logger, configuration_fetcher),
|
66
66
|
Commands::RunPublishCI.new(bind, push_local_to_staging, build_and_push_tarball),
|
67
67
|
Commands::Tag.new(logger, configuration_fetcher, version_control_system),
|
68
|
-
Commands::UpdateLocalDocRepos.new(
|
68
|
+
Commands::UpdateLocalDocRepos.new(
|
69
|
+
colored_streams,
|
70
|
+
configuration_fetcher,
|
71
|
+
version_control_system,
|
72
|
+
local_file_system_accessor
|
73
|
+
),
|
69
74
|
]
|
70
75
|
end
|
71
76
|
|
@@ -75,7 +80,7 @@ module Bookbinder
|
|
75
80
|
|
76
81
|
def bind
|
77
82
|
@bind ||= Commands::Bind.new(
|
78
|
-
|
83
|
+
colored_streams,
|
79
84
|
OutputLocations.new(final_app_dir: final_app_directory, context_dir: File.absolute_path('.')),
|
80
85
|
Config::BindConfigFactory.new(version_control_system, configuration_fetcher),
|
81
86
|
Config::ArchiveMenuConfiguration.new(loader: config_loader, config_filename: 'bookbinder.yml'),
|
@@ -112,10 +117,15 @@ module Bookbinder
|
|
112
117
|
)
|
113
118
|
end
|
114
119
|
|
120
|
+
def colored_streams
|
121
|
+
{ out: $stdout,
|
122
|
+
success: Streams::ColorizedStream.new(Colorizer::Colors.green, $stdout),
|
123
|
+
err: Streams::ColorizedStream.new(Colorizer::Colors.red, $stderr) }
|
124
|
+
end
|
125
|
+
|
115
126
|
def configuration_fetcher
|
116
127
|
@configuration_fetcher ||= Config::Fetcher.new(
|
117
|
-
|
118
|
-
Config::Validator.new(logger, local_file_system_accessor),
|
128
|
+
Config::Validator.new(local_file_system_accessor),
|
119
129
|
config_loader,
|
120
130
|
Config::RemoteYamlCredentialProvider.new(logger, version_control_system)
|
121
131
|
).tap do |fetcher|
|
@@ -7,8 +7,8 @@ module Bookbinder
|
|
7
7
|
class UpdateLocalDocRepos
|
8
8
|
include Commands::Naming
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
10
|
+
def initialize(streams, configuration_fetcher, version_control_system, filesystem)
|
11
|
+
@streams = streams
|
12
12
|
@configuration_fetcher = configuration_fetcher
|
13
13
|
@version_control_system = version_control_system
|
14
14
|
@filesystem = filesystem
|
@@ -22,19 +22,28 @@ module Bookbinder
|
|
22
22
|
def run(_)
|
23
23
|
urls = configuration_fetcher.fetch_config.sections.map(&:repo_url)
|
24
24
|
paths(urls).each do |path|
|
25
|
-
|
26
|
-
|
27
|
-
version_control_system.update(path)
|
28
|
-
else
|
29
|
-
logger.log ' skipping (not found) '.magenta + path
|
30
|
-
end
|
25
|
+
streams[:out] << "\nUpdating #{path}:"
|
26
|
+
report(version_control_system.update(path))
|
31
27
|
end
|
28
|
+
streams[:out].puts
|
32
29
|
0
|
33
30
|
end
|
34
31
|
|
35
32
|
private
|
36
33
|
|
37
|
-
attr_reader
|
34
|
+
attr_reader(:streams,
|
35
|
+
:configuration_fetcher,
|
36
|
+
:version_control_system,
|
37
|
+
:filesystem)
|
38
|
+
|
39
|
+
def report(result)
|
40
|
+
messages = { true => "updated", false => "skipping (#{result.reason})" }
|
41
|
+
streams[stream_types[result.success?]] << " #{messages[result.success?]}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def stream_types
|
45
|
+
{ true => :success, false => :out }
|
46
|
+
end
|
38
47
|
|
39
48
|
def paths(urls)
|
40
49
|
urls.map {|url| File.absolute_path("../#{Ingest::DestinationDirectory.new(url)}")}
|
@@ -12,11 +12,9 @@ module Bookbinder
|
|
12
12
|
|
13
13
|
def check(config)
|
14
14
|
partial_location = './master_middleman/source/archive_menus/_default.erb'
|
15
|
-
if config.
|
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)
|
15
|
+
if config.archive_menu && config.archive_menu.include?(nil)
|
18
16
|
EmptyArchiveItemsError.new 'Did you forget to add a value to the archive_menu?'
|
19
|
-
elsif config.
|
17
|
+
elsif config.archive_menu && !file_system_accessor.file_exist?(partial_location)
|
20
18
|
MissingArchiveMenuPartialError.new "You must provide a template partial named at #{partial_location}"
|
21
19
|
end
|
22
20
|
end
|
@@ -25,10 +23,6 @@ module Bookbinder
|
|
25
23
|
|
26
24
|
attr_reader :file_system_accessor
|
27
25
|
|
28
|
-
def archive_items(config)
|
29
|
-
config.fetch('archive_menu', [])
|
30
|
-
end
|
31
|
-
|
32
26
|
end
|
33
27
|
end
|
34
28
|
end
|
@@ -5,18 +5,25 @@ module Bookbinder
|
|
5
5
|
DitamapLocationError = Class.new(RuntimeError)
|
6
6
|
|
7
7
|
def check(config)
|
8
|
-
dita_sections
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
8
|
+
if none_with_pred?(dita_sections(config)) { |s|
|
9
|
+
s.preprocessor_config['ditamap_location']
|
10
|
+
}
|
11
|
+
DitamapLocationError.new(
|
12
|
+
"You must have at least one 'ditamap_location' key in dita_sections."
|
13
|
+
)
|
15
14
|
end
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
private
|
18
|
+
|
19
|
+
def none_with_pred?(coll, &block)
|
20
|
+
coll.any? && coll.none?(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dita_sections(config)
|
24
|
+
config.sections.select {|s|
|
25
|
+
s.preprocessor_config.has_key?('ditamap_location')
|
26
|
+
}
|
20
27
|
end
|
21
28
|
end
|
22
29
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative '../../ingest/destination_directory'
|
2
|
+
|
1
3
|
module Bookbinder
|
2
4
|
module Config
|
3
5
|
module Checkers
|
@@ -13,8 +15,10 @@ module Bookbinder
|
|
13
15
|
private
|
14
16
|
|
15
17
|
def duplicate_section_names?(config)
|
16
|
-
|
17
|
-
|
18
|
+
directory_names = config.sections.map {|section|
|
19
|
+
Ingest::DestinationDirectory.new(section.repo_name,
|
20
|
+
section.desired_directory_name)
|
21
|
+
}
|
18
22
|
directory_names.length != directory_names.uniq.length
|
19
23
|
end
|
20
24
|
|
@@ -5,14 +5,11 @@ module Bookbinder
|
|
5
5
|
MissingRepositoryNameError = Class.new(RuntimeError)
|
6
6
|
|
7
7
|
def check(config)
|
8
|
-
|
9
|
-
|
10
|
-
if !section['repository'] || !section['repository']['name']
|
11
|
-
true
|
12
|
-
end
|
8
|
+
failures = config.sections.reject do |section|
|
9
|
+
section.repo_name
|
13
10
|
end
|
14
11
|
|
15
|
-
if failures.
|
12
|
+
if failures.empty?
|
16
13
|
nil
|
17
14
|
else
|
18
15
|
MissingRepositoryNameError.new error_message
|
@@ -5,42 +5,22 @@ module Bookbinder
|
|
5
5
|
module Checkers
|
6
6
|
class RequiredKeysChecker
|
7
7
|
MissingRequiredKeyError = Class.new(RuntimeError)
|
8
|
-
SectionAbsenceError = Class.new(RuntimeError)
|
9
8
|
|
10
9
|
def check(config)
|
11
10
|
missing_keys = []
|
12
11
|
|
13
|
-
Config::Configuration::CONFIG_REQUIRED_KEYS.
|
14
|
-
|
15
|
-
|
12
|
+
Config::Configuration::CONFIG_REQUIRED_KEYS.each do |required_key|
|
13
|
+
begin
|
14
|
+
config.public_send(required_key)
|
15
|
+
rescue KeyError
|
16
16
|
missing_keys.push(required_key)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
if missing_keys.length > 0
|
21
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
22
|
end
|
25
23
|
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
24
|
end
|
45
25
|
end
|
46
26
|
end
|
@@ -6,9 +6,8 @@ require_relative 'yaml_loader'
|
|
6
6
|
module Bookbinder
|
7
7
|
module Config
|
8
8
|
class Fetcher
|
9
|
-
def initialize(
|
9
|
+
def initialize(configuration_validator, loader, credentials_provider)
|
10
10
|
@loader = loader
|
11
|
-
@logger = logger
|
12
11
|
@configuration_validator = configuration_validator
|
13
12
|
@credentials_provider = credentials_provider
|
14
13
|
end
|
@@ -36,7 +35,7 @@ module Bookbinder
|
|
36
35
|
|
37
36
|
private
|
38
37
|
|
39
|
-
attr_reader(:loader, :
|
38
|
+
attr_reader(:loader, :configuration_validator, :config, :config_file_path,
|
40
39
|
:credentials_provider)
|
41
40
|
|
42
41
|
def read_config_file
|
@@ -51,10 +50,10 @@ module Bookbinder
|
|
51
50
|
def validate(config_hash)
|
52
51
|
raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
Configuration.parse(config_hash).tap do |config|
|
54
|
+
errors = configuration_validator.exceptions(config)
|
55
|
+
raise errors.first if errors.any?
|
56
|
+
end
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|
@@ -28,7 +28,7 @@ module Bookbinder
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def preprocessor_config
|
31
|
-
config
|
31
|
+
config.fetch('preprocessor_config', {})
|
32
32
|
end
|
33
33
|
|
34
34
|
def ==(other)
|
@@ -46,7 +46,7 @@ module Bookbinder
|
|
46
46
|
private
|
47
47
|
|
48
48
|
def repo
|
49
|
-
config
|
49
|
+
config.fetch('repository', {})
|
50
50
|
end
|
51
51
|
|
52
52
|
attr_reader :config
|
@@ -7,12 +7,11 @@ require_relative 'checkers/dita_section_checker'
|
|
7
7
|
module Bookbinder
|
8
8
|
module Config
|
9
9
|
class Validator
|
10
|
-
def initialize(
|
11
|
-
@logger = logger
|
10
|
+
def initialize(file_system_accessor)
|
12
11
|
@file_system_accessor = file_system_accessor
|
13
12
|
end
|
14
13
|
|
15
|
-
def exceptions(
|
14
|
+
def exceptions(config)
|
16
15
|
exceptions = [
|
17
16
|
Checkers::RequiredKeysChecker.new,
|
18
17
|
Checkers::DuplicateSectionNameChecker.new,
|
@@ -20,7 +19,7 @@ module Bookbinder
|
|
20
19
|
Checkers::DitaSectionChecker.new,
|
21
20
|
Checkers::ArchiveMenuChecker.new(@file_system_accessor)
|
22
21
|
].map do |checker|
|
23
|
-
checker.check(
|
22
|
+
checker.check(config)
|
24
23
|
end
|
25
24
|
|
26
25
|
exceptions.compact
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'git'
|
2
|
+
require_relative 'update_failure'
|
3
|
+
require_relative 'update_success'
|
4
|
+
|
5
|
+
module Bookbinder
|
6
|
+
module Ingest
|
7
|
+
class GitAccessor
|
8
|
+
TagExists = Class.new(RuntimeError)
|
9
|
+
InvalidTagRef = Class.new(RuntimeError)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@cache = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def clone(url, name, path: nil, checkout: 'master')
|
16
|
+
cached_clone(url, name, path).tap do |git|
|
17
|
+
git.checkout(checkout)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(cloned_path)
|
22
|
+
Git.open(cloned_path).pull
|
23
|
+
Ingest::UpdateSuccess.new
|
24
|
+
rescue ArgumentError, Git::GitExecuteError => e
|
25
|
+
case e.message
|
26
|
+
when /overwritten by merge/
|
27
|
+
Ingest::UpdateFailure.new('merge error')
|
28
|
+
when /path does not exist/
|
29
|
+
Ingest::UpdateFailure.new('not found')
|
30
|
+
else
|
31
|
+
raise
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def read_file(filename, from_repo: nil, checkout: 'master')
|
36
|
+
Dir.mktmpdir do |dir|
|
37
|
+
path = Pathname(dir)
|
38
|
+
git = _clone(from_repo, temp_name("read-file"), path)
|
39
|
+
git.checkout(checkout)
|
40
|
+
path.join(temp_name("read-file"), filename).read
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def remote_tag(url, tagname, commit_or_object)
|
45
|
+
Dir.mktmpdir do |dir|
|
46
|
+
path = Pathname(dir)
|
47
|
+
git = _clone(url, temp_name("tag"), path)
|
48
|
+
git.config('user.name', 'Bookbinder')
|
49
|
+
git.config('user.email', 'bookbinder@cloudfoundry.org')
|
50
|
+
begin
|
51
|
+
git.add_tag(tagname, "origin/#{commit_or_object}",
|
52
|
+
message: 'Tagged by Bookbinder')
|
53
|
+
rescue Git::GitExecuteError => e
|
54
|
+
case e.message
|
55
|
+
when /already exists/
|
56
|
+
raise TagExists
|
57
|
+
when /as a valid ref/
|
58
|
+
raise InvalidTagRef
|
59
|
+
else
|
60
|
+
raise
|
61
|
+
end
|
62
|
+
end
|
63
|
+
git.push("origin", "master", tags: true)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
attr_reader :cache
|
70
|
+
|
71
|
+
def temp_name(purpose)
|
72
|
+
"bookbinder-git-accessor-#{purpose}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def cached_clone(url, name, path)
|
76
|
+
cache[[url, name, path]] ||= _clone(url, name, path)
|
77
|
+
end
|
78
|
+
|
79
|
+
def _clone(url, name, path)
|
80
|
+
Git.clone(url, name, path: path)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -6,8 +6,8 @@ module Bookbinder
|
|
6
6
|
class SitemapWriter
|
7
7
|
def self.build(logger, final_app_directory, port)
|
8
8
|
new(
|
9
|
-
Spider.new(
|
10
|
-
ServerDirector.new(
|
9
|
+
Spider.new(app_dir: final_app_directory),
|
10
|
+
ServerDirector.new(directory: final_app_directory, port: port)
|
11
11
|
)
|
12
12
|
end
|
13
13
|
|
@@ -16,9 +16,14 @@ module Bookbinder
|
|
16
16
|
@server_director = server_director
|
17
17
|
end
|
18
18
|
|
19
|
-
def write(host_for_sitemap)
|
19
|
+
def write(host_for_sitemap, streams, broken_link_exclusions)
|
20
20
|
server_director.use_server { |port|
|
21
|
-
spider.generate_sitemap(
|
21
|
+
spider.generate_sitemap(
|
22
|
+
host_for_sitemap,
|
23
|
+
port,
|
24
|
+
streams,
|
25
|
+
broken_link_exclusions: broken_link_exclusions
|
26
|
+
)
|
22
27
|
}.tap do |sitemap|
|
23
28
|
File.write(sitemap.to_path, sitemap.to_xml)
|
24
29
|
end
|
data/lib/bookbinder/spider.rb
CHANGED
@@ -15,7 +15,7 @@ module Bookbinder
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def has_broken_links?
|
18
|
-
@broken_links.any?
|
18
|
+
@broken_links.any?
|
19
19
|
end
|
20
20
|
|
21
21
|
def to_xml
|
@@ -27,46 +27,46 @@ module Bookbinder
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
|
30
|
+
def self.prepend_location(location, url)
|
31
|
+
"#{URI(location).path} => #{url}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(app_dir: nil)
|
32
35
|
@app_dir = app_dir || raise('Spiders must be initialized with an app directory.')
|
33
|
-
@broken_links = []
|
34
36
|
end
|
35
37
|
|
36
|
-
def generate_sitemap(target_host, port
|
38
|
+
def generate_sitemap(target_host, port, streams,
|
39
|
+
broken_link_exclusions: /(?!.*)/)
|
37
40
|
temp_host = "localhost:#{port}"
|
38
41
|
|
39
42
|
sieve = Sieve.new domain: "http://#{temp_host}"
|
40
43
|
links = crawl_from "http://#{temp_host}/index.html", sieve
|
41
44
|
broken_links, working_links = links
|
42
|
-
|
43
|
-
announce_broken_links broken_links
|
44
|
-
|
45
45
|
sitemap_links = substitute_hostname(temp_host, target_host, working_links)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
public_broken_links = broken_links.reject {|l| l.match(broken_link_exclusions)}
|
47
|
+
announce_broken_links(public_broken_links, streams)
|
48
|
+
Result.new(
|
49
|
+
public_broken_links,
|
50
|
+
SitemapGenerator.new.generate(sitemap_links), app_dir
|
51
|
+
)
|
51
52
|
end
|
52
53
|
|
53
54
|
private
|
54
55
|
|
55
|
-
|
56
|
-
if broken_links.any?
|
57
|
-
@logger.error "\nFound #{broken_links.count} broken links!"
|
58
|
-
|
59
|
-
broken_links.each do |link|
|
60
|
-
if link.include?('#')
|
61
|
-
@logger.warn(link)
|
62
|
-
else
|
63
|
-
@logger.notify(link)
|
64
|
-
end
|
65
|
-
end
|
56
|
+
attr_reader :app_dir
|
66
57
|
|
67
|
-
|
58
|
+
def announce_broken_links(broken_links, streams)
|
59
|
+
if broken_links.none?
|
60
|
+
streams[:out].puts "\nNo broken links!"
|
68
61
|
else
|
69
|
-
|
62
|
+
streams[:err].puts(<<-MESSAGE)
|
63
|
+
|
64
|
+
Found #{broken_links.count} broken links!
|
65
|
+
|
66
|
+
#{broken_links.sort.join("\n")}
|
67
|
+
|
68
|
+
Found #{broken_links.count} broken links!
|
69
|
+
MESSAGE
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -70,14 +70,10 @@ module Bookbinder
|
|
70
70
|
private
|
71
71
|
|
72
72
|
def subnav_template_name
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
config[:subnav_templates].has_key?(ns)
|
78
|
-
}
|
79
|
-
config[:subnav_templates][template_key] || 'default'
|
80
|
-
end
|
73
|
+
template_key = decreasingly_specific_namespaces.detect { |ns|
|
74
|
+
config[:subnav_templates].has_key?(ns)
|
75
|
+
}
|
76
|
+
config[:subnav_templates][template_key] || 'default'
|
81
77
|
end
|
82
78
|
|
83
79
|
def decreasingly_specific_namespaces
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bookbindery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Grafton
|
@@ -15,7 +15,7 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: install_bin
|
17
17
|
cert_chain: []
|
18
|
-
date: 2015-06-
|
18
|
+
date: 2015-06-24 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: fog-aws
|
@@ -311,16 +311,18 @@ files:
|
|
311
311
|
- lib/bookbinder/dita_html_to_middleman_formatter.rb
|
312
312
|
- lib/bookbinder/errors/cli_error.rb
|
313
313
|
- lib/bookbinder/errors/programmer_mistake.rb
|
314
|
-
- lib/bookbinder/git_accessor.rb
|
315
314
|
- lib/bookbinder/html_document_manipulator.rb
|
316
315
|
- lib/bookbinder/ingest/cloner_factory.rb
|
317
316
|
- lib/bookbinder/ingest/destination_directory.rb
|
317
|
+
- lib/bookbinder/ingest/git_accessor.rb
|
318
318
|
- lib/bookbinder/ingest/git_cloner.rb
|
319
319
|
- lib/bookbinder/ingest/local_filesystem_cloner.rb
|
320
320
|
- lib/bookbinder/ingest/missing_working_copy.rb
|
321
321
|
- lib/bookbinder/ingest/repo_identifier.rb
|
322
322
|
- lib/bookbinder/ingest/section_repository.rb
|
323
323
|
- lib/bookbinder/ingest/section_repository_factory.rb
|
324
|
+
- lib/bookbinder/ingest/update_failure.rb
|
325
|
+
- lib/bookbinder/ingest/update_success.rb
|
324
326
|
- lib/bookbinder/ingest/working_copy.rb
|
325
327
|
- lib/bookbinder/local_file_system_accessor.rb
|
326
328
|
- lib/bookbinder/middleman_runner.rb
|
@@ -1,70 +0,0 @@
|
|
1
|
-
require 'git'
|
2
|
-
|
3
|
-
module Bookbinder
|
4
|
-
class GitAccessor
|
5
|
-
TagExists = Class.new(RuntimeError)
|
6
|
-
InvalidTagRef = Class.new(RuntimeError)
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@cache = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def clone(url, name, path: nil, checkout: 'master')
|
13
|
-
cached_clone(url, name, path).tap do |git|
|
14
|
-
git.checkout(checkout)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def update(cloned_path)
|
19
|
-
Git.open(cloned_path).pull
|
20
|
-
end
|
21
|
-
|
22
|
-
def read_file(filename, from_repo: nil, checkout: 'master')
|
23
|
-
Dir.mktmpdir do |dir|
|
24
|
-
path = Pathname(dir)
|
25
|
-
git = _clone(from_repo, temp_name("read-file"), path)
|
26
|
-
git.checkout(checkout)
|
27
|
-
path.join(temp_name("read-file"), filename).read
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def remote_tag(url, tagname, commit_or_object)
|
32
|
-
Dir.mktmpdir do |dir|
|
33
|
-
path = Pathname(dir)
|
34
|
-
git = _clone(url, temp_name("tag"), path)
|
35
|
-
git.config('user.name', 'Bookbinder')
|
36
|
-
git.config('user.email', 'bookbinder@cloudfoundry.org')
|
37
|
-
begin
|
38
|
-
git.add_tag(tagname, "origin/#{commit_or_object}",
|
39
|
-
message: 'Tagged by Bookbinder')
|
40
|
-
rescue Git::GitExecuteError => e
|
41
|
-
case e.message
|
42
|
-
when /already exists/
|
43
|
-
raise TagExists
|
44
|
-
when /as a valid ref/
|
45
|
-
raise InvalidTagRef
|
46
|
-
else
|
47
|
-
raise
|
48
|
-
end
|
49
|
-
end
|
50
|
-
git.push("origin", "master", tags: true)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
attr_reader :cache
|
57
|
-
|
58
|
-
def temp_name(purpose)
|
59
|
-
"bookbinder-git-accessor-#{purpose}"
|
60
|
-
end
|
61
|
-
|
62
|
-
def cached_clone(url, name, path)
|
63
|
-
cache[[url, name, path]] ||= _clone(url, name, path)
|
64
|
-
end
|
65
|
-
|
66
|
-
def _clone(url, name, path)
|
67
|
-
Git.clone(url, name, path: path)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|