bookbindery 1.0.3 → 2.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/{bin → install_bin}/bookbinder +0 -0
  3. data/lib/bookbinder.rb +2 -9
  4. data/lib/bookbinder/archive.rb +1 -1
  5. data/lib/bookbinder/archive_menu_configuration.rb +34 -0
  6. data/lib/bookbinder/book.rb +17 -17
  7. data/lib/bookbinder/cli.rb +25 -36
  8. data/lib/bookbinder/code_example.rb +5 -38
  9. data/lib/bookbinder/code_example_reader.rb +40 -0
  10. data/lib/bookbinder/colorizer.rb +14 -0
  11. data/lib/bookbinder/command_runner.rb +9 -16
  12. data/lib/bookbinder/command_validator.rb +14 -7
  13. data/lib/bookbinder/commands/bind.rb +321 -0
  14. data/lib/bookbinder/commands/build_and_push_tarball.rb +7 -6
  15. data/lib/bookbinder/commands/chain.rb +11 -0
  16. data/lib/bookbinder/commands/generate_pdf.rb +4 -3
  17. data/lib/bookbinder/commands/help.rb +49 -10
  18. data/lib/bookbinder/commands/naming.rb +9 -1
  19. data/lib/bookbinder/commands/push_local_to_staging.rb +4 -3
  20. data/lib/bookbinder/commands/push_to_prod.rb +36 -4
  21. data/lib/bookbinder/commands/run_publish_ci.rb +21 -24
  22. data/lib/bookbinder/commands/tag.rb +3 -3
  23. data/lib/bookbinder/commands/update_local_doc_repos.rb +5 -4
  24. data/lib/bookbinder/commands/version.rb +11 -8
  25. data/lib/bookbinder/configuration.rb +8 -3
  26. data/lib/bookbinder/configuration_fetcher.rb +7 -25
  27. data/lib/bookbinder/configuration_validator.rb +21 -0
  28. data/lib/bookbinder/distributor.rb +1 -1
  29. data/lib/bookbinder/dita_html_to_middleman_formatter.rb +37 -0
  30. data/lib/bookbinder/dita_section.rb +7 -0
  31. data/lib/bookbinder/dita_section_gatherer.rb +28 -0
  32. data/lib/bookbinder/git_accessor.rb +17 -0
  33. data/lib/bookbinder/git_client.rb +10 -7
  34. data/lib/bookbinder/git_hub_repository.rb +46 -41
  35. data/lib/bookbinder/local_dita_preprocessor.rb +27 -0
  36. data/lib/bookbinder/local_dita_to_html_converter.rb +49 -0
  37. data/lib/bookbinder/local_file_system_accessor.rb +68 -0
  38. data/lib/bookbinder/middleman_runner.rb +30 -17
  39. data/lib/bookbinder/publisher.rb +16 -80
  40. data/lib/bookbinder/remote_yaml_credential_provider.rb +2 -3
  41. data/lib/bookbinder/repositories/command_repository.rb +156 -0
  42. data/lib/bookbinder/repositories/section_repository.rb +31 -0
  43. data/lib/bookbinder/section.rb +5 -67
  44. data/lib/bookbinder/shell_out.rb +1 -0
  45. data/lib/bookbinder/sheller.rb +19 -0
  46. data/lib/bookbinder/sieve.rb +6 -1
  47. data/lib/bookbinder/terminal.rb +10 -0
  48. data/lib/bookbinder/user_message.rb +6 -0
  49. data/lib/bookbinder/user_message_presenter.rb +21 -0
  50. data/lib/bookbinder/yaml_loader.rb +18 -7
  51. data/master_middleman/archive_drop_down_menu.rb +46 -0
  52. data/master_middleman/bookbinder_helpers.rb +47 -40
  53. metadata +33 -87
  54. data/lib/bookbinder/commands/publish.rb +0 -138
  55. data/lib/bookbinder/usage_messenger.rb +0 -33
@@ -2,11 +2,12 @@ require 'middleman-core'
2
2
  require 'middleman-core/cli'
3
3
  require 'middleman-core/profiling'
4
4
  require_relative 'code_example'
5
+ require_relative 'code_example_reader'
5
6
 
6
7
  class Middleman::Cli::BuildAction
7
8
  def handle_error(file_name, response, e=Thor::Error.new(response))
8
9
  our_errors = [Bookbinder::GitClient::TokenException,
9
- Bookbinder::CodeExample::InvalidSnippet,
10
+ Bookbinder::CodeExampleReader::InvalidSnippet,
10
11
  QuicklinksRenderer::BadHeadingLevelError,
11
12
  Git::GitExecuteError]
12
13
  raise e if our_errors.include?(e.class)
@@ -31,20 +32,36 @@ end
31
32
 
32
33
  module Bookbinder
33
34
  class MiddlemanRunner
34
- def initialize(logger)
35
+ def initialize(logger, git_accessor)
35
36
  @logger = logger
37
+ @git_accessor = git_accessor
36
38
  end
37
39
 
38
- def run(middleman_dir, template_variables, local_repo_dir, verbose = false, book = nil, sections = [], production_host=nil, archive_menu=nil, git_accessor=Git)
40
+ def run(middleman_dir,
41
+ workspace_dir,
42
+ template_variables,
43
+ local_repo_dir,
44
+ verbose = false,
45
+ subnav_templates_by_directory = {},
46
+ production_host=nil,
47
+ archive_menu=nil)
39
48
  @logger.log "\nRunning middleman...\n\n"
40
49
 
41
50
  within(middleman_dir) do
42
- invoke_against_current_dir(local_repo_dir, production_host, book, sections, template_variables, archive_menu, verbose, git_accessor)
51
+ invoke_against_current_dir(local_repo_dir,
52
+ workspace_dir,
53
+ production_host,
54
+ subnav_templates_by_directory,
55
+ template_variables,
56
+ archive_menu,
57
+ verbose)
43
58
  end
44
59
  end
45
60
 
46
61
  private
47
62
 
63
+ attr_reader :git_accessor
64
+
48
65
  def within(temp_root, &block)
49
66
  Middleman::Cli::Build.instance_variable_set(:@_shared_instance, nil)
50
67
  original_mm_root = ENV['MM_ROOT']
@@ -55,32 +72,28 @@ module Bookbinder
55
72
  ENV['MM_ROOT'] = original_mm_root
56
73
  end
57
74
 
58
- def invoke_against_current_dir(local_repo_dir, production_host, book, sections, template_variables, archive_menu, verbose, git_accessor)
75
+ def invoke_against_current_dir(local_repo_dir,
76
+ workspace_dir,
77
+ production_host,
78
+ subnav_templates,
79
+ template_variables,
80
+ archive_menu,
81
+ verbose)
59
82
  builder = Middleman::Cli::Build.shared_instance(verbose)
60
83
 
61
84
  config = {
62
85
  local_repo_dir: local_repo_dir,
86
+ workspace: workspace_dir,
63
87
  production_host: production_host,
64
88
  git_accessor: git_accessor,
65
- sections: sections,
66
- book: book,
67
89
  template_variables: template_variables,
68
90
  relative_links: false,
69
- subnav_templates: subnavs_by_dir_name(sections),
91
+ subnav_templates: subnav_templates,
70
92
  archive_menu: archive_menu
71
93
  }
72
94
 
73
95
  config.each { |k, v| builder.config[k] = v }
74
96
  Middleman::Cli::Build.new([], {quiet: !verbose}, {}).invoke :build, [], {verbose: verbose}
75
97
  end
76
-
77
- def subnavs_by_dir_name(sections)
78
- sections.reduce({}) do |final_map, section|
79
- namespace = section.directory.gsub('/', '_')
80
- template = section.subnav_template || 'default'
81
-
82
- final_map.merge(namespace => template)
83
- end
84
- end
85
98
  end
86
99
  end
@@ -1,43 +1,35 @@
1
1
  require 'middleman-syntax'
2
2
  require_relative 'bookbinder_logger'
3
3
  require_relative 'directory_helpers'
4
- require_relative 'section'
4
+ require_relative 'repositories/section_repository'
5
5
  require_relative 'server_director'
6
6
 
7
7
  module Bookbinder
8
8
  class Publisher
9
9
  include DirectoryHelperMethods
10
10
 
11
- def initialize(logger, spider, static_site_generator)
12
- @gem_root = File.expand_path('../../../', __FILE__)
11
+ def initialize(logger, spider, static_site_generator, server_director, file_system_accessor)
13
12
  @logger = logger
14
13
  @spider = spider
15
14
  @static_site_generator = static_site_generator
15
+ @server_director = server_director
16
+ @file_system_accessor = file_system_accessor
16
17
  end
17
18
 
18
- def publish(cli_options, output_paths, publish_config, git_accessor)
19
+ def publish(subnavs, cli_options, output_paths, publish_config)
19
20
  intermediate_directory = output_paths.fetch(:output_dir)
20
21
  final_app_dir = output_paths.fetch(:final_app_dir)
21
- master_middleman_dir = output_paths.fetch(:master_middleman_dir)
22
22
  master_dir = File.join intermediate_directory, 'master_middleman'
23
23
  workspace_dir = File.join master_dir, 'source'
24
24
  build_directory = File.join master_dir, 'build/.'
25
25
  public_directory = File.join final_app_dir, 'public'
26
26
 
27
- @versions = publish_config.fetch(:versions, [])
28
- @book_repo = publish_config[:book_repo]
29
- prepare_directories final_app_dir, intermediate_directory, workspace_dir, master_middleman_dir, master_dir, git_accessor
30
27
  FileUtils.cp 'redirects.rb', final_app_dir if File.exists?('redirects.rb')
31
28
 
32
- target_tag = cli_options[:target_tag]
33
- sections = gather_sections(workspace_dir, publish_config, output_paths, target_tag, git_accessor)
34
- book = Book.new(logger: @logger,
35
- full_name: @book_repo,
36
- sections: publish_config.fetch(:sections))
37
29
  host_for_sitemap = publish_config.fetch(:host_for_sitemap)
38
30
 
39
- generate_site(cli_options, output_paths, publish_config, master_dir, book, sections, build_directory, public_directory, git_accessor)
40
- generate_sitemap(final_app_dir, host_for_sitemap, @spider)
31
+ generate_site(cli_options, output_paths, publish_config, master_dir, workspace_dir, subnavs, build_directory, public_directory)
32
+ generate_sitemap(host_for_sitemap, @spider)
41
33
 
42
34
  @logger.log "Bookbinder bound your book into #{final_app_dir.to_s.green}"
43
35
 
@@ -46,80 +38,24 @@ module Bookbinder
46
38
 
47
39
  private
48
40
 
49
- def generate_sitemap(final_app_dir, host_for_sitemap, spider)
50
- server_director = ServerDirector.new(@logger, directory: final_app_dir)
41
+ attr_reader :section_repository, :logger, :file_system_accessor
42
+
43
+ def generate_sitemap(host_for_sitemap, spider)
51
44
  raise "Your public host must be a single String." unless host_for_sitemap.is_a?(String)
52
45
 
53
- server_director.use_server { |port| spider.generate_sitemap host_for_sitemap, port }
46
+ @server_director.use_server { |port| spider.generate_sitemap host_for_sitemap, port }
54
47
  end
55
48
 
56
- def generate_site(cli_options, output_paths, publish_config, middleman_dir, book, sections, build_dir, public_dir, git_accessor)
49
+ def generate_site(cli_options, output_paths, publish_config, middleman_dir, workspace_dir, subnavs, build_dir, public_dir)
57
50
  @static_site_generator.run(middleman_dir,
51
+ workspace_dir,
58
52
  publish_config.fetch(:template_variables, {}),
59
53
  output_paths[:local_repo_dir],
60
54
  cli_options[:verbose],
61
- book,
62
- sections,
55
+ subnavs,
63
56
  publish_config[:host_for_sitemap],
64
- publish_config[:archive_menu],
65
- git_accessor
66
- )
67
- FileUtils.cp_r build_dir, public_dir
68
- end
69
-
70
- def gather_sections(workspace, publish_config, output_paths, target_tag, git_accessor)
71
- section_data = publish_config.fetch(:sections)
72
- section_data.map do |attributes|
73
- section = Section.get_instance(@logger,
74
- section_hash: attributes,
75
- destination_dir: workspace,
76
- local_repo_dir: output_paths[:local_repo_dir],
77
- target_tag: target_tag,
78
- git_accessor: git_accessor)
79
- section
80
- end
81
- end
82
-
83
- def prepare_directories(final_app, middleman_scratch_space, middleman_source, master_middleman_dir, middleman_dir, git_accessor)
84
- forget_sections(middleman_scratch_space)
85
- FileUtils.rm_rf File.join final_app, '.'
86
- FileUtils.mkdir_p middleman_scratch_space
87
- FileUtils.mkdir_p File.join final_app, 'public'
88
- FileUtils.mkdir_p middleman_source
89
-
90
- copy_directory_from_gem 'template_app', final_app
91
- copy_directory_from_gem 'master_middleman', middleman_dir
92
- FileUtils.cp_r File.join(master_middleman_dir, '.'), middleman_dir
93
-
94
- copy_version_master_middleman(middleman_source, git_accessor)
95
- end
96
-
97
- # Copy the index file from each version into the version's directory. Because version
98
- # subdirectories are sections, this is the only way they get content from their master
99
- # middleman directory.
100
- def copy_version_master_middleman(dest_dir, git_accessor)
101
- @versions.each do |version|
102
- Dir.mktmpdir(version) do |tmpdir|
103
- book = Book.from_remote(logger: @logger, full_name: @book_repo,
104
- destination_dir: tmpdir, ref: version, git_accessor: git_accessor)
105
- index_source_dir = File.join(tmpdir, book.directory, 'master_middleman', source_dir_name)
106
- index_dest_dir = File.join(dest_dir, version)
107
- FileUtils.mkdir_p(index_dest_dir)
108
-
109
- Dir.glob(File.join(index_source_dir, 'index.*')) do |f|
110
- FileUtils.cp(File.expand_path(f), index_dest_dir)
111
- end
112
- end
113
- end
114
- end
115
-
116
- def forget_sections(middleman_scratch)
117
- Section.store.clear
118
- FileUtils.rm_rf File.join middleman_scratch, '.'
119
- end
120
-
121
- def copy_directory_from_gem(dir, output_dir)
122
- FileUtils.cp_r File.join(@gem_root, "#{dir}/."), output_dir
57
+ publish_config[:archive_menu])
58
+ file_system_accessor.copy build_dir, public_dir
123
59
  end
124
60
  end
125
61
  end
@@ -3,16 +3,15 @@ require 'tempfile'
3
3
 
4
4
  module Bookbinder
5
5
  class RemoteYamlCredentialProvider
6
- def initialize(logger, repository, git_accessor = Git)
6
+ def initialize(logger, repository)
7
7
  @logger = logger
8
8
  @repository = repository
9
- @git_accessor = git_accessor
10
9
  end
11
10
 
12
11
  def credentials
13
12
  @logger.log "Processing #{@repository.full_name.cyan}"
14
13
  Dir.mktmpdir do |destination_dir|
15
- @repository.copy_from_remote(destination_dir, @git_accessor)
14
+ @repository.copy_from_remote(destination_dir)
16
15
  cred_file_yaml = File.join(destination_dir, @repository.short_name, 'credentials.yml')
17
16
  YAML.load_file(cred_file_yaml)
18
17
  end
@@ -0,0 +1,156 @@
1
+ Dir.glob(File.expand_path('../../commands/*.rb', __FILE__)).each do |command_file|
2
+ require command_file
3
+ end
4
+ require_relative '../configuration_fetcher'
5
+ require_relative '../configuration_validator'
6
+ require_relative '../dita_html_to_middleman_formatter'
7
+ require_relative '../git_accessor'
8
+ require_relative '../local_dita_to_html_converter'
9
+ require_relative '../local_dita_preprocessor'
10
+ require_relative '../local_file_system_accessor'
11
+ require_relative '../middleman_runner'
12
+ require_relative '../spider'
13
+ require_relative '../yaml_loader'
14
+
15
+ module Bookbinder
16
+ module Repositories
17
+ class CommandRepository
18
+ include Enumerable
19
+
20
+ def initialize(logger)
21
+ @logger = logger
22
+ end
23
+
24
+ def each(&block)
25
+ list.each(&block)
26
+ end
27
+
28
+ def help
29
+ @help ||= Commands::Help.new(
30
+ logger,
31
+ [version] + standard_commands
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :logger
38
+
39
+ def list
40
+ standard_commands + flags
41
+ end
42
+
43
+ def flags
44
+ @flags ||= [ version, help ]
45
+ end
46
+
47
+ def standard_commands
48
+ @standard_commands ||= [
49
+ build_and_push_tarball,
50
+ Commands::GeneratePDF.new(logger, configuration_fetcher),
51
+ bind,
52
+ push_local_to_staging,
53
+ Commands::PushToProd.new(logger, configuration_fetcher),
54
+ Commands::RunPublishCI.new(bind, push_local_to_staging, build_and_push_tarball),
55
+ Commands::Tag.new(logger, configuration_fetcher),
56
+ Commands::UpdateLocalDocRepos.new(logger, configuration_fetcher),
57
+ ]
58
+ end
59
+
60
+ def version
61
+ @version ||= Commands::Version.new(logger)
62
+ end
63
+
64
+ def bind
65
+ @bind ||= Commands::Bind.new(
66
+ logger,
67
+ configuration_fetcher,
68
+ ArchiveMenuConfiguration.new(
69
+ loader: config_loader,
70
+ config_filename: 'bookbinder.yml'
71
+ ),
72
+ git_accessor,
73
+ local_file_system_accessor,
74
+ middleman_runner,
75
+ spider,
76
+ final_app_directory,
77
+ server_director,
78
+ File.absolute_path('.'),
79
+ dita_preprocessor
80
+ )
81
+ end
82
+
83
+ def push_local_to_staging
84
+ @push_local_to_staging ||= Commands::PushLocalToStaging.new(
85
+ logger,
86
+ configuration_fetcher
87
+ )
88
+ end
89
+
90
+ def build_and_push_tarball
91
+ @build_and_push_tarball ||= Commands::BuildAndPushTarball.new(
92
+ logger,
93
+ configuration_fetcher
94
+ )
95
+ end
96
+
97
+ def spider
98
+ @spider ||= Spider.new(logger, app_dir: final_app_directory)
99
+ end
100
+
101
+ def server_director
102
+ @server_director ||= ServerDirector.new(
103
+ logger,
104
+ directory: final_app_directory
105
+ )
106
+ end
107
+
108
+ def middleman_runner
109
+ @middleman_runner ||= MiddlemanRunner.new(logger, git_accessor)
110
+ end
111
+
112
+ def configuration_fetcher
113
+ @configuration_fetcher ||= ConfigurationFetcher.new(
114
+ logger,
115
+ ConfigurationValidator.new(logger, local_file_system_accessor),
116
+ config_loader
117
+ ).tap do |fetcher|
118
+ fetcher.set_config_file_path './config.yml'
119
+ end
120
+ end
121
+
122
+ def config_loader
123
+ @config_loader ||= YAMLLoader.new
124
+ end
125
+
126
+ def final_app_directory
127
+ @final_app_directory ||= File.absolute_path('final_app')
128
+ end
129
+
130
+ def dita_preprocessor
131
+ @dita_preprocessor ||=
132
+ LocalDitaPreprocessor.new(local_dita_processor,
133
+ dita_html_to_middleman_formatter,
134
+ local_file_system_accessor)
135
+ end
136
+
137
+ def local_dita_processor
138
+ @local_dita_processor ||=
139
+ LocalDitaToHtmlConverter.new(Sheller.new(logger),
140
+ ENV['PATH_TO_DITA_OT_LIBRARY'])
141
+ end
142
+
143
+ def dita_html_to_middleman_formatter
144
+ @dita_html_to_middleman_formatter ||= DitaHtmlToMiddlemanFormatter.new(local_file_system_accessor)
145
+ end
146
+
147
+ def git_accessor
148
+ @git_accessor ||= GitAccessor.new
149
+ end
150
+
151
+ def local_file_system_accessor
152
+ @local_file_system_accessor ||= LocalFileSystemAccessor.new
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,31 @@
1
+ require 'tmpdir'
2
+
3
+ module Bookbinder
4
+ module Repositories
5
+ class SectionRepository
6
+ def initialize(logger)
7
+ @logger = logger
8
+ end
9
+
10
+ def get_instance(attributes,
11
+ vcs_repo: nil,
12
+ destination_dir: Dir.mktmpdir,
13
+ build: nil)
14
+ repository_config = attributes['repository']
15
+ raise "section repository '#{repository_config}' is not a hash" unless repository_config.is_a?(Hash)
16
+ raise "section repository '#{repository_config}' missing name key" unless repository_config['name']
17
+ logger.log "Gathering #{repository_config['name'].cyan}"
18
+ build[vcs_repo.copied_to,
19
+ vcs_repo.full_name,
20
+ vcs_repo.copied?,
21
+ attributes['subnav_template'],
22
+ destination_dir,
23
+ vcs_repo.directory]
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :logger
29
+ end
30
+ end
31
+ end
@@ -1,78 +1,16 @@
1
- require 'bookbinder/directory_helpers'
2
-
3
1
  module Bookbinder
4
- class Section
5
-
6
- def self.store
7
- @@store ||= {}
8
- end
9
-
10
- def self.get_instance(logger,
11
- section_hash: {},
12
- local_repo_dir: nil,
13
- destination_dir: Dir.mktmpdir,
14
- target_tag: nil,
15
- git_accessor: Git)
16
- @git_accessor = git_accessor
17
- store.fetch([section_hash, local_repo_dir]) { acquire(logger, section_hash, local_repo_dir, destination_dir, target_tag, git_accessor) }
18
- end
19
2
 
20
- def initialize(logger, repository, subnav_template)
21
- @logger = logger
22
- @subnav_template = subnav_template
23
- @repository = repository
24
- @git_accessor = Git
3
+ Section = Struct.new(:path_to_repository, :full_name, :copied, :subnav_templ, :destination_dir, :directory_name) do
4
+ def path_to_repository
5
+ Pathname(self[:path_to_repository].to_s)
25
6
  end
26
7
 
27
8
  def subnav_template
28
- @subnav_template.gsub(/^_/, '').gsub(/\.erb$/, '') if @subnav_template
9
+ subnav_templ.sub(/^_/, '').sub(/\.erb$/, '') if subnav_templ
29
10
  end
30
11
 
31
12
  def directory
32
- @repository.directory
33
- end
34
-
35
- def full_name
36
- @repository.full_name
37
- end
38
-
39
- def copied?
40
- @repository.copied?
41
- end
42
-
43
- def get_modification_date_for(file: nil, full_path: nil)
44
- unless @repository.has_git_object?
45
- begin
46
- git_base_object = @git_accessor.open(@repository.path_to_local_repo)
47
- rescue => e
48
- raise "Invalid git repository! Cannot get modification date for section: #{@repository.path_to_local_repo}."
49
- end
50
- end
51
- @repository.get_modification_date_for(file: file, git: git_base_object)
52
- end
53
-
54
- private
55
-
56
- def self.acquire(logger, section_hash, local_repo_dir, destination, target_tag, git_accessor)
57
- repository = section_hash['repository']
58
- raise "section repository '#{repository}' is not a hash" unless repository.is_a?(Hash)
59
- raise "section repository '#{repository}' missing name key" unless repository['name']
60
- logger.log "Gathering #{repository['name'].cyan}"
61
-
62
- repository = build_repository(logger, destination, local_repo_dir, section_hash, target_tag, git_accessor)
63
- section = new(logger, repository, section_hash['subnav_template'])
64
-
65
- store[[section_hash, local_repo_dir]] = section
66
- end
67
- private_class_method :acquire
68
-
69
- def self.build_repository(logger, destination, local_repo_dir, repo_hash, target_tag, git_accessor)
70
- if local_repo_dir
71
- GitHubRepository.build_from_local(logger, repo_hash, local_repo_dir, destination)
72
- else
73
- GitHubRepository.build_from_remote(logger, repo_hash, destination, target_tag, git_accessor)
74
- end
13
+ directory_name
75
14
  end
76
- private_class_method :build_repository
77
15
  end
78
16
  end