bookbindery 1.0.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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