bookbindery 2.0.1 → 2.1.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bookbinder.rb +2 -2
  3. data/lib/bookbinder/app_fetcher.rb +2 -6
  4. data/lib/bookbinder/archive.rb +1 -1
  5. data/lib/bookbinder/book.rb +7 -2
  6. data/lib/bookbinder/cli.rb +4 -5
  7. data/lib/bookbinder/code_example_reader.rb +1 -1
  8. data/lib/bookbinder/command_validator.rb +30 -10
  9. data/lib/bookbinder/commands/bind.rb +58 -53
  10. data/lib/bookbinder/commands/help.rb +4 -4
  11. data/lib/bookbinder/commands/naming.rb +14 -4
  12. data/lib/bookbinder/commands/push_from_local.rb +51 -0
  13. data/lib/bookbinder/commands/push_to_prod.rb +1 -2
  14. data/lib/bookbinder/commands/run_publish_ci.rb +1 -1
  15. data/lib/bookbinder/commands/tag.rb +1 -1
  16. data/lib/bookbinder/commands/version.rb +5 -4
  17. data/lib/bookbinder/configuration.rb +49 -28
  18. data/lib/bookbinder/configuration_fetcher.rb +1 -1
  19. data/lib/bookbinder/configuration_validator.rb +40 -37
  20. data/lib/bookbinder/{bookbinder_logger.rb → deprecated_logger.rb} +1 -1
  21. data/lib/bookbinder/distributor.rb +16 -9
  22. data/lib/bookbinder/dita_html_to_middleman_formatter.rb +26 -8
  23. data/lib/bookbinder/dita_section.rb +8 -2
  24. data/lib/bookbinder/git_hub_repository.rb +7 -20
  25. data/lib/bookbinder/html_document_manipulator.rb +21 -0
  26. data/lib/bookbinder/ingest/cloner_factory.rb +25 -0
  27. data/lib/bookbinder/ingest/git_hub_repository_cloner_facade.rb +26 -0
  28. data/lib/bookbinder/ingest/local_filesystem_cloner_facade.rb +28 -0
  29. data/lib/bookbinder/local_dita_preprocessor.rb +33 -5
  30. data/lib/bookbinder/local_dita_to_html_converter.rb +26 -28
  31. data/lib/bookbinder/local_file_system_accessor.rb +0 -5
  32. data/lib/bookbinder/publisher.rb +1 -1
  33. data/lib/bookbinder/remote_yaml_credential_provider.rb +1 -1
  34. data/lib/bookbinder/repositories/command_repository.rb +21 -5
  35. data/lib/bookbinder/section.rb +12 -1
  36. data/lib/bookbinder/subnav.rb +4 -0
  37. data/lib/bookbinder/subnav_formatter.rb +37 -0
  38. data/lib/bookbinder/user_message.rb +10 -1
  39. data/master_middleman/bookbinder_helpers.rb +3 -3
  40. metadata +46 -40
  41. data/lib/bookbinder/commands/push_local_to_staging.rb +0 -36
@@ -38,10 +38,9 @@ module Bookbinder
38
38
  build_number: arguments[0],
39
39
 
40
40
  aws_credentials: config.aws_credentials,
41
- cf_credentials: config.cf_production_credentials,
41
+ cf_credentials: config.cf_credentials('production'),
42
42
 
43
43
  book_repo: config.book_repo,
44
- production: true
45
44
  }
46
45
  end
47
46
 
@@ -2,7 +2,7 @@ require_relative 'bind'
2
2
  require_relative 'build_and_push_tarball'
3
3
  require_relative 'chain'
4
4
  require_relative 'naming'
5
- require_relative 'push_local_to_staging'
5
+ require_relative 'push_from_local'
6
6
 
7
7
  module Bookbinder
8
8
  module Commands
@@ -1,6 +1,6 @@
1
1
  require_relative '../book'
2
- require_relative '../bookbinder_logger'
3
2
  require_relative '../cli_error'
3
+ require_relative '../deprecated_logger'
4
4
 
5
5
  require_relative 'bookbinder_command'
6
6
  require_relative 'naming'
@@ -9,10 +9,6 @@ module Bookbinder
9
9
  @logger = logger
10
10
  end
11
11
 
12
- def command_name
13
- '--version'
14
- end
15
-
16
12
  def usage
17
13
  [command_name, "Print the version of bookbinder"]
18
14
  end
@@ -23,6 +19,11 @@ module Bookbinder
23
19
  0
24
20
  end
25
21
 
22
+ private
23
+
24
+ def command_name
25
+ '--version'
26
+ end
26
27
  end
27
28
  end
28
29
  end
@@ -1,6 +1,6 @@
1
- require_relative 'remote_yaml_credential_provider'
2
- require_relative 'git_hub_repository'
3
1
  require 'git'
2
+ require_relative 'git_hub_repository'
3
+ require_relative 'remote_yaml_credential_provider'
4
4
 
5
5
  module Bookbinder
6
6
  class Configuration
@@ -8,14 +8,11 @@ module Bookbinder
8
8
  CURRENT_SCHEMA_VERSION = '1.0.0'
9
9
  STARTING_SCHEMA_VERSION = '1.0.0'
10
10
 
11
- class CredentialKeyError < StandardError;
12
- end
13
-
14
- class ConfigSchemaUnsupportedError < StandardError;
15
- end
11
+ CredentialKeyError = Class.new(StandardError)
12
+ ConfigSchemaUnsupportedError = Class.new(StandardError)
16
13
 
17
14
  class AwsCredentials
18
- REQUIRED_KEYS = %w(access_key secret_key green_builds_bucket).freeze
15
+ REQUIRED_KEYS = %w(access_key secret_key green_builds_bucket)
19
16
 
20
17
  def initialize(cred_hash)
21
18
  @creds = cred_hash
@@ -37,12 +34,11 @@ module Bookbinder
37
34
  end
38
35
 
39
36
  class CfCredentials
40
- REQUIRED_KEYS = %w(api_endpoint organization app_name).freeze
41
- OPTIONAL_KEYS = %w(username password production_space production_host staging_space staging_host).freeze
37
+ REQUIRED_KEYS = %w(api_endpoint organization app_name)
42
38
 
43
- def initialize(cred_hash, is_production)
39
+ def initialize(cred_hash, environment)
44
40
  @creds = cred_hash
45
- @is_production = is_production
41
+ @environment = environment
46
42
  end
47
43
 
48
44
  REQUIRED_KEYS.each do |method_name|
@@ -51,15 +47,33 @@ module Bookbinder
51
47
  end
52
48
  end
53
49
 
54
- OPTIONAL_KEYS.each do |method_name|
55
- define_method(method_name) do
56
- creds.fetch(method_name, nil)
50
+ def ==(other)
51
+ [@creds, @environment] == [
52
+ other.instance_variable_get(:@creds),
53
+ other.instance_variable_get(:@environment)
54
+ ]
55
+ end
56
+
57
+ def username
58
+ creds['username']
59
+ end
60
+
61
+ def password
62
+ creds['password']
63
+ end
64
+
65
+ def download_archive_before_push?
66
+ production?
67
+ end
68
+
69
+ def push_warning
70
+ if production?
71
+ 'Warning: You are pushing to CF Docs production. Be careful.'
57
72
  end
58
73
  end
59
74
 
60
75
  def routes
61
- key = is_production ? 'production_host' : 'staging_host'
62
- fetch(key) if correctly_formatted_domain_and_routes?(key)
76
+ fetch(host_key) if correctly_formatted_domain_and_routes?(host_key)
63
77
  end
64
78
 
65
79
  def flat_routes
@@ -70,13 +84,16 @@ module Bookbinder
70
84
  end
71
85
 
72
86
  def space
73
- key = is_production ? 'production_space' : 'staging_space'
74
- fetch(key)
87
+ fetch(space_key)
75
88
  end
76
89
 
77
90
  private
78
91
 
79
- attr_reader :creds, :is_production
92
+ attr_reader :creds, :environment
93
+
94
+ def production?
95
+ environment == 'production'
96
+ end
80
97
 
81
98
  def fetch(key)
82
99
  creds.fetch(key)
@@ -97,6 +114,14 @@ module Bookbinder
97
114
  raise "Hosts in credentials must be nested as an array under the desired domain #{domain}." unless routes_hash[domain].is_a? Array
98
115
  raise "Did you mean to provide a hostname for the domain #{domain}? Check your credentials.yml." if routes_hash[domain].any?(&:nil?)
99
116
  end
117
+
118
+ def host_key
119
+ "#{environment}_host"
120
+ end
121
+
122
+ def space_key
123
+ "#{environment}_space"
124
+ end
100
125
  end
101
126
 
102
127
  attr_reader :schema_version, :schema_major_version, :schema_minor_version, :schema_patch_version
@@ -106,8 +131,8 @@ module Bookbinder
106
131
  @config = config_hash
107
132
  end
108
133
 
109
- CONFIG_REQUIRED_KEYS = %w(book_repo public_host pdf)
110
- CONFIG_OPTIONAL_KEYS = %w(archive_menu dita_sections layout_repo versions pdf_index cred_repo)
134
+ CONFIG_REQUIRED_KEYS = %w(book_repo public_host)
135
+ CONFIG_OPTIONAL_KEYS = %w(archive_menu dita_sections layout_repo versions pdf_index cred_repo pdf)
111
136
 
112
137
  CONFIG_REQUIRED_KEYS.each do |method_name|
113
138
  define_method(method_name) do
@@ -137,12 +162,8 @@ module Bookbinder
137
162
  @aws_creds ||= AwsCredentials.new(credentials.fetch('aws'))
138
163
  end
139
164
 
140
- def cf_staging_credentials
141
- @cf_staging_creds ||= CfCredentials.new(credentials.fetch('cloud_foundry'), false)
142
- end
143
-
144
- def cf_production_credentials
145
- @cf_prod_creds ||= CfCredentials.new(credentials.fetch('cloud_foundry'), true)
165
+ def cf_credentials(environment)
166
+ CfCredentials.new(credentials.fetch('cloud_foundry'), environment)
146
167
  end
147
168
 
148
169
  def ==(o)
@@ -14,7 +14,7 @@ module Bookbinder
14
14
  end
15
15
 
16
16
  def set_config_file_path config_file_path
17
- @config_file_path = config_file_path
17
+ @config_file_path = File.expand_path config_file_path
18
18
  end
19
19
 
20
20
  private
@@ -1,6 +1,40 @@
1
1
  require 'active_support/core_ext/object/blank'
2
2
 
3
3
  module Bookbinder
4
+ class ConfigurationValidator
5
+ DuplicateSectionNameError = Class.new(RuntimeError)
6
+ MissingArchiveMenuPartialError = Class.new(RuntimeError)
7
+ EmptyArchiveItemsError = Class.new(RuntimeError)
8
+ ArchiveMenuNotDefinedError = Class.new(RuntimeError)
9
+ MissingRequiredKeyError = Class.new(RuntimeError)
10
+
11
+ def initialize(logger, file_system_accessor)
12
+ @logger = logger
13
+ @file_system_accessor = file_system_accessor
14
+ end
15
+
16
+ def valid?(config_hash, bookbinder_schema_version, starting_schema_version)
17
+ raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
18
+
19
+ user_config_schema_version = config_hash['schema_version']
20
+ exceptions = [
21
+ RequiredKeysChecker.new,
22
+ ConfigVersionChecker.new(Version.parse(bookbinder_schema_version),
23
+ Version.parse(starting_schema_version),
24
+ VersionCheckerMessages.new(Version.parse(user_config_schema_version),
25
+ bookbinder_schema_version),
26
+ @logger),
27
+ DuplicateSectionNameChecker.new,
28
+ ArchiveMenuChecker.new(@file_system_accessor)
29
+ ].map do |checker|
30
+ checker.check(config_hash)
31
+ end
32
+ exception = exceptions.compact.first
33
+ raise exception if exception
34
+
35
+ true
36
+ end
37
+ end
4
38
 
5
39
  class DuplicateSectionNameChecker
6
40
  def check(config)
@@ -12,8 +46,12 @@ module Bookbinder
12
46
  private
13
47
 
14
48
  def duplicate_section_names?(config)
15
- directory_names = config['sections'].map {|section| section['directory']}
16
- directory_names.length != directory_names.uniq.length
49
+ if config['sections']
50
+ directory_names = config['sections'].map {|section| section['directory']}
51
+ directory_names.length != directory_names.uniq.length
52
+ else
53
+ false
54
+ end
17
55
  end
18
56
 
19
57
  end
@@ -147,39 +185,4 @@ module Bookbinder
147
185
  end
148
186
  end
149
187
  end
150
-
151
- class ConfigurationValidator
152
- DuplicateSectionNameError = Class.new(RuntimeError)
153
- MissingArchiveMenuPartialError = Class.new(RuntimeError)
154
- EmptyArchiveItemsError = Class.new(RuntimeError)
155
- ArchiveMenuNotDefinedError = Class.new(RuntimeError)
156
- MissingRequiredKeyError = Class.new(RuntimeError)
157
-
158
- def initialize(logger, file_system_accessor)
159
- @logger = logger
160
- @file_system_accessor = file_system_accessor
161
- end
162
-
163
- def valid?(config_hash, bookbinder_schema_version, starting_schema_version)
164
- raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
165
-
166
- user_config_schema_version = config_hash['schema_version']
167
- exceptions = [
168
- RequiredKeysChecker.new,
169
- ConfigVersionChecker.new(Version.parse(bookbinder_schema_version),
170
- Version.parse(starting_schema_version),
171
- VersionCheckerMessages.new(Version.parse(user_config_schema_version),
172
- bookbinder_schema_version),
173
- @logger),
174
- DuplicateSectionNameChecker.new,
175
- ArchiveMenuChecker.new(@file_system_accessor)
176
- ].map do |checker|
177
- checker.check(config_hash)
178
- end
179
- exception = exceptions.compact.first
180
- raise exception if exception
181
-
182
- true
183
- end
184
- end
185
188
  end
@@ -5,7 +5,7 @@ class String
5
5
  end
6
6
 
7
7
  module Bookbinder
8
- class BookbinderLogger
8
+ class DeprecatedLogger
9
9
  def log(message)
10
10
  puts message
11
11
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'app_fetcher'
2
+ require_relative 'artifact_namer'
2
3
 
3
4
  module Bookbinder
4
5
  class Distributor
@@ -27,14 +28,18 @@ module Bookbinder
27
28
 
28
29
  def distribute
29
30
  begin
30
- download if options[:production]
31
+ download if cf_credentials.download_archive_before_push?
31
32
  push_app
32
33
  nil
33
34
  rescue => e
34
- cf_credentials = options[:cf_credentials]
35
- cf_space = options[:production] ? cf_credentials.production_space : cf_credentials.staging_space
36
- cf_routes = options[:production] ? cf_credentials.production_host : cf_credentials.staging_host
37
- @logger.error "[ERROR] #{e.message}\n[DEBUG INFO]\nCF organization: #{cf_credentials.organization}\nCF space: #{cf_space}\nCF account: #{cf_credentials.username}\nroutes: #{cf_routes}"
35
+ @logger.error(<<-ERROR.chomp)
36
+ [ERROR] #{e.message}
37
+ [DEBUG INFO]
38
+ CF organization: #{cf_credentials.organization}
39
+ CF space: #{cf_credentials.space}
40
+ CF account: #{cf_credentials.username}
41
+ routes: #{cf_credentials.routes}
42
+ ERROR
38
43
  ensure
39
44
  upload_trace
40
45
  end
@@ -45,12 +50,14 @@ module Bookbinder
45
50
  attr_reader :options, :archive, :namer, :namespace, :pusher
46
51
 
47
52
  def download
48
- archive.download(download_dir: options[:app_dir], bucket: options[:aws_credentials].green_builds_bucket, build_number: options[:build_number],
53
+ archive.download(download_dir: options[:app_dir],
54
+ bucket: options[:aws_credentials].green_builds_bucket,
55
+ build_number: options[:build_number],
49
56
  namespace: namespace)
50
57
  end
51
58
 
52
59
  def push_app
53
- warn if options[:production]
60
+ @logger.warn(cf_credentials.push_warning) if cf_credentials.push_warning
54
61
  pusher.push(options[:app_dir])
55
62
  end
56
63
 
@@ -62,8 +69,8 @@ module Bookbinder
62
69
  @logger.error "Could not find CF trace file: #{namer.full_path}"
63
70
  end
64
71
 
65
- def warn
66
- @logger.warn 'Warning: You are pushing to CF Docs production. Be careful.'
72
+ def cf_credentials
73
+ options[:cf_credentials]
67
74
  end
68
75
  end
69
76
  end
@@ -1,19 +1,24 @@
1
+ require_relative 'subnav'
2
+
1
3
  module Bookbinder
2
4
 
3
5
  class DitaHtmlToMiddlemanFormatter
4
- def initialize(file_system_accessor)
6
+ def initialize(file_system_accessor, subnav_formatter, html_document_manipulator)
5
7
  @file_system_accessor = file_system_accessor
8
+ @subnav_formatter = subnav_formatter
9
+ @html_document_manipulator = html_document_manipulator
6
10
  end
7
11
 
8
- def format(src, dest)
12
+ def format_html(src, dest)
9
13
  all_files_with_ext = file_system_accessor.find_files_with_ext('.html', src)
10
14
 
11
15
  all_files_with_ext.map do |filepath|
12
- file_title_text = file_system_accessor.read_html_in_tag(path: filepath,
13
- marker: 'title')
16
+ file_text = file_system_accessor.read filepath
17
+ file_title_text = html_document_manipulator.read_html_in_tag(document: file_text,
18
+ tag: 'title')
14
19
 
15
- file_body_text = file_system_accessor.read_html_in_tag(path: filepath,
16
- marker: 'body')
20
+ file_body_text = html_document_manipulator.read_html_in_tag(document: file_text,
21
+ tag: 'body')
17
22
 
18
23
  relative_path_to_file = file_system_accessor.relative_path_from(src, filepath)
19
24
  new_filepath = File.join dest, "#{relative_path_to_file}.erb"
@@ -24,14 +29,27 @@ module Bookbinder
24
29
  end
25
30
  end
26
31
 
32
+ def format_subnav(dita_section,
33
+ subnav_template_text,
34
+ json_props_location,
35
+ unformatted_subnav_text)
36
+ formatted_json_links = subnav_formatter.get_links_as_json(unformatted_subnav_text, dita_section.directory)
37
+
38
+ nav_text = html_document_manipulator.set_attribute(document: subnav_template_text,
39
+ selector: 'div.nav-content',
40
+ attribute: 'data-props-location',
41
+ value: json_props_location)
42
+ Subnav.new(formatted_json_links, nav_text)
43
+ end
44
+
27
45
  private
28
46
 
29
- attr_reader :file_system_accessor
47
+ attr_reader :file_system_accessor, :subnav_formatter, :html_document_manipulator
30
48
 
31
49
  def frontmatter(title)
32
50
  sanitized_title = title.gsub('"', '\"')
33
51
  "---\ntitle: \"#{sanitized_title}\"\ndita: true\n---\n"
34
52
  end
35
-
36
53
  end
54
+
37
55
  end
@@ -3,5 +3,11 @@ module Bookbinder
3
3
  :ditamap_location,
4
4
  :full_name,
5
5
  :target_ref,
6
- :directory)
7
- end
6
+ :directory) do
7
+ def subnav
8
+ namespace = directory.gsub('/', '_')
9
+ template = "#{directory}_subnav"
10
+ {namespace => template}
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,7 @@
1
- require 'bookbinder/shell_out'
2
1
  require 'git'
3
- require_relative 'bookbinder_logger'
2
+ require_relative 'deprecated_logger'
4
3
  require_relative 'git_client'
4
+ require_relative 'shell_out'
5
5
 
6
6
  module Bookbinder
7
7
  class GitHubRepository
@@ -13,14 +13,11 @@ module Bookbinder
13
13
 
14
14
  def self.build_from_remote(logger,
15
15
  section_hash,
16
- target_ref,
17
16
  git_accessor)
18
17
  full_name = section_hash.fetch('repository', {}).fetch('name')
19
- target_ref = target_ref || section_hash.fetch('repository', {})['ref']
20
18
  directory = section_hash['directory']
21
19
  new(logger: logger,
22
20
  full_name: full_name,
23
- target_ref: target_ref,
24
21
  github_token: ENV['GITHUB_API_TOKEN'],
25
22
  directory: directory,
26
23
  git_accessor: git_accessor)
@@ -42,7 +39,6 @@ module Bookbinder
42
39
 
43
40
  def initialize(logger: nil,
44
41
  full_name: nil,
45
- target_ref: nil,
46
42
  github_token: nil,
47
43
  directory: nil,
48
44
  local_repo_dir: nil,
@@ -50,7 +46,6 @@ module Bookbinder
50
46
  @logger = logger
51
47
  raise 'No full_name provided ' unless full_name
52
48
  @full_name = full_name
53
- @target_ref = target_ref
54
49
  @directory = directory
55
50
  @local_repo_dir = local_repo_dir
56
51
 
@@ -75,7 +70,7 @@ module Bookbinder
75
70
  @directory || short_name
76
71
  end
77
72
 
78
- def copy_from_remote(destination_dir)
73
+ def copy_from_remote(destination_dir, target_ref)
79
74
  begin
80
75
  @git_base_object = git_accessor.clone("git@github.com:#{full_name}",
81
76
  directory,
@@ -131,22 +126,14 @@ module Bookbinder
131
126
  @logger.log ' skipping (not found) '.magenta + path_to_local_repo
132
127
  end
133
128
 
134
- def path_to_local_repo
135
- if @local_repo_dir
136
- File.join(@local_repo_dir, short_name)
137
- end
138
- end
139
-
140
- def has_git_object?
141
- !!@git_base_object
142
- end
143
-
144
129
  private
145
130
 
146
131
  attr_reader :git_accessor
147
132
 
148
- def target_ref
149
- @target_ref ||= 'master'
133
+ def path_to_local_repo
134
+ if @local_repo_dir
135
+ File.join(@local_repo_dir, short_name)
136
+ end
150
137
  end
151
138
 
152
139
  def tags