bookbindery 3.1.2 → 4.0.0
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 +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
|