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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 07f71ad52807a1f8b6a85bd211883dd095c8262f
4
- data.tar.gz: 7a6157d356da84da49236483507377e2c393c671
3
+ metadata.gz: 56679c8bf251f348c3dd66d86317c0c848b35706
4
+ data.tar.gz: e6a280ed228236c2fa381393d5bdeb5f3710f8e1
5
5
  SHA512:
6
- metadata.gz: 8eef0dce3fe1834608202017de1eb0ef06caf7638a70d329a97f19f08d3420399e2eac074cdf3b9f51ae71d7070ef54e85c53bbb00dbd35fd708cc61d4314592
7
- data.tar.gz: f0dff29b6cb65e19bf479f636fa22108a9e06e8ecd113ae5baf7316e036703332a0d1304064064efe6db352ede94cb49a1ca9e44dfdf9b2fc940d02b4b1499d3
6
+ metadata.gz: b28159d61a3847f29100709af210253bc25e90ed5770273d4891ea928a24f643f7537832c36e8e3efe2780b5dfdab773b4dd3c41ab994a3ce4c8cddfcedc9cb9
7
+ data.tar.gz: 3463e88ed1b0044e735c0e0ffd25c572b4258cc0906985f0b74c6bfca47d1e2269895700f4ca07c694c83c2a4ee25268be1c9dd9be2e5c8284cb286564510097
File without changes
@@ -1,10 +1,7 @@
1
- require 'fog'
1
+ require 'fog/aws'
2
2
  require 'tmpdir'
3
3
  require 'ansi'
4
- require 'faraday'
5
- require 'faraday_middleware'
6
4
  require 'octokit'
7
- require 'padrino-contrib'
8
5
  require 'middleman-syntax'
9
6
  require 'middleman-core/cli'
10
7
  require 'middleman-core/profiling'
@@ -14,10 +11,8 @@ require 'vienna'
14
11
  require 'popen4'
15
12
  require 'puma'
16
13
 
17
- #require_relative 'bookbinder/shell_out'
18
14
  require_relative 'bookbinder/bookbinder_logger'
19
15
  require_relative 'bookbinder/git_client'
20
- #require_relative 'bookbinder/repository'
21
16
  require_relative 'bookbinder/section'
22
17
  require_relative 'bookbinder/book'
23
18
  require_relative 'bookbinder/code_example'
@@ -43,7 +38,7 @@ require_relative 'bookbinder/distributor'
43
38
 
44
39
  require_relative 'bookbinder/commands/bookbinder_command'
45
40
  require_relative 'bookbinder/commands/build_and_push_tarball'
46
- require_relative 'bookbinder/commands/publish'
41
+ require_relative 'bookbinder/commands/bind'
47
42
  require_relative 'bookbinder/commands/push_local_to_staging'
48
43
  require_relative 'bookbinder/commands/push_to_prod'
49
44
  require_relative 'bookbinder/commands/run_publish_ci'
@@ -51,8 +46,6 @@ require_relative 'bookbinder/commands/update_local_doc_repos'
51
46
  require_relative 'bookbinder/commands/tag'
52
47
  require_relative 'bookbinder/commands/generate_pdf'
53
48
 
54
- require_relative 'bookbinder/usage_messenger'
55
-
56
49
  require_relative 'bookbinder/cli'
57
50
 
58
51
  # Finds the project root for both spec & production
@@ -1,4 +1,4 @@
1
- require 'fog'
1
+ require 'fog/aws'
2
2
  require 'tmpdir'
3
3
  require_relative 'bookbinder_logger'
4
4
  require_relative 'artifact_namer'
@@ -0,0 +1,34 @@
1
+ module Bookbinder
2
+ class ArchiveMenuConfiguration
3
+ def initialize(loader: nil, config_filename: nil)
4
+ @loader = loader
5
+ @config_filename = config_filename
6
+ end
7
+
8
+ def generate(base_config, sections)
9
+ base_config.merge(
10
+ archive_menu: root_config(base_config).merge(section_config(sections))
11
+ )
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :loader, :config_filename
17
+
18
+ def root_config(base_config)
19
+ { '.' => base_config[:archive_menu] }
20
+ end
21
+
22
+ def section_config(sections)
23
+ sections.reduce({}) {|config, section|
24
+ config_path = section.path_to_repository.join(config_filename)
25
+ archive_config = loader.load_key(config_path, 'archive_menu')
26
+ if archive_config
27
+ config.merge(section.directory_name => archive_config)
28
+ else
29
+ config
30
+ end
31
+ }
32
+ end
33
+ end
34
+ end
@@ -4,7 +4,6 @@ require_relative 'directory_helpers'
4
4
  module Bookbinder
5
5
  class Book
6
6
  include DirectoryHelperMethods
7
- attr_reader :sections
8
7
 
9
8
  def self.from_remote(logger: nil, full_name: nil, destination_dir: nil, ref: nil, git_accessor: Git)
10
9
  book = new(logger: logger, full_name: full_name, target_ref: ref, git_accessor: git_accessor)
@@ -12,12 +11,23 @@ module Bookbinder
12
11
  book
13
12
  end
14
13
 
15
- def initialize(logger: nil, full_name: nil, target_ref: nil, github_token: nil, sections: [], git_accessor: Git)
16
- @sections = sections.map do |section|
17
- GitHubRepository.new logger: logger, full_name: section['repository']['name']
14
+ def initialize(logger: nil,
15
+ full_name: nil,
16
+ target_ref: nil,
17
+ github_token: nil,
18
+ sections: [],
19
+ git_accessor: Git)
20
+ @section_vcs_repos = sections.map do |section|
21
+ GitHubRepository.new(logger: logger,
22
+ full_name: section['repository']['name'],
23
+ git_accessor: git_accessor)
18
24
  end
19
25
 
20
- @repository = GitHubRepository.new(logger: logger, full_name: full_name, target_ref: target_ref, github_token: github_token)
26
+ @repository = GitHubRepository.new(logger: logger,
27
+ full_name: full_name,
28
+ target_ref: target_ref,
29
+ github_token: github_token,
30
+ git_accessor: git_accessor)
21
31
  @git_accessor = git_accessor
22
32
  end
23
33
 
@@ -33,22 +43,12 @@ module Bookbinder
33
43
  @repository.directory
34
44
  end
35
45
 
36
- def get_modification_date_for(file: nil, full_path: nil)
37
- git_directory, file_relative_path = full_path.split(output_dir_name+'/')
38
- begin
39
- git_base_object = Git.open(git_directory)
40
- rescue => e
41
- raise "Invalid git repository! #{git_directory} is not a .git directory"
42
- end
43
- @repository.get_modification_date_for(file: file_relative_path, git: git_base_object)
44
- end
45
-
46
46
  def copy_from_remote(destination_dir)
47
- @repository.copy_from_remote(destination_dir, @git_accessor)
47
+ @repository.copy_from_remote(destination_dir)
48
48
  end
49
49
 
50
50
  def tag_self_and_sections_with(tag)
51
- (@sections + [@repository]).each { |repo| repo.tag_with tag }
51
+ (@section_vcs_repos + [@repository]).each { |repo| repo.tag_with tag }
52
52
  end
53
53
  end
54
54
  end
@@ -1,53 +1,42 @@
1
+ require_relative 'colorizer'
1
2
  require_relative 'command_runner'
2
- require_relative 'local_file_system_accessor'
3
3
  require_relative 'command_validator'
4
-
5
- require_relative 'commands/build_and_push_tarball'
6
- require_relative 'commands/generate_pdf'
7
- require_relative 'commands/publish'
8
- require_relative 'commands/version'
9
- require_relative 'commands/help'
4
+ require_relative 'configuration'
5
+ require_relative 'repositories/command_repository'
6
+ require_relative 'terminal'
7
+ require_relative 'user_message_presenter'
10
8
 
11
9
  module Bookbinder
12
10
  class Cli
13
- FLAGS = [
14
- Commands::Version,
15
- Commands::Help
16
- ]
17
-
18
- COMMANDS = [
19
- Commands::BuildAndPushTarball,
20
- Commands::GeneratePDF,
21
- Commands::Publish,
22
- Commands::PushLocalToStaging,
23
- Commands::PushToProd,
24
- Commands::RunPublishCI,
25
- Commands::Tag,
26
- Commands::UpdateLocalDocRepos,
27
- ].freeze
28
-
29
11
  def run(args)
30
12
  command_name = args[0]
31
13
  command_arguments = args[1..-1]
32
14
 
33
15
  logger = BookbinderLogger.new
34
- yaml_loader = YAMLLoader.new
35
- local_file_system_accessor = LocalFileSystemAccessor.new
36
- configuration_validator = ConfigurationValidator.new(logger, local_file_system_accessor)
37
- configuration_fetcher = ConfigurationFetcher.new(logger, configuration_validator, yaml_loader)
38
- configuration_fetcher.set_config_file_path './config.yml'
39
- usage_messenger = UsageMessenger.new
40
- usage_message = usage_messenger.construct_for(COMMANDS, FLAGS)
41
- command_validator = CommandValidator.new usage_messenger, COMMANDS + FLAGS, usage_message
42
-
43
- command_runner = CommandRunner.new(configuration_fetcher, usage_message, logger, COMMANDS + FLAGS)
16
+ commands = Repositories::CommandRepository.new(logger)
17
+
18
+ command_validator = CommandValidator.new(commands, commands.help.usage_message)
19
+ command_runner = CommandRunner.new(logger, commands)
20
+ command_name = command_name ? command_name : '--help'
21
+
22
+ colorizer = Colorizer.new
23
+ user_message_presenter = UserMessagePresenter.new(colorizer)
24
+ terminal = Terminal.new
25
+
26
+ user_message = command_validator.validate(command_name)
27
+ if user_message.escalation_type == EscalationType.error
28
+ error_message = user_message_presenter.get_error(user_message)
29
+ terminal.update(error_message)
30
+ return 1
31
+ elsif user_message.escalation_type == EscalationType.warn
32
+ warning_message = user_message_presenter.get_warning(user_message)
33
+ terminal.update(warning_message)
34
+ end
44
35
 
45
36
  begin
46
- command_name ? command_validator.validate!(command_name) : command_name = '--help'
47
-
48
37
  command_runner.run command_name, command_arguments
49
38
 
50
- rescue Commands::Publish::VersionUnsupportedError => e
39
+ rescue Commands::Bind::VersionUnsupportedError => e
51
40
  logger.error "config.yml at version '#{e.message}' has an unsupported API."
52
41
  1
53
42
  rescue Configuration::CredentialKeyError => e
@@ -1,40 +1,7 @@
1
- require_relative 'section'
2
-
3
1
  module Bookbinder
4
- class CodeExample < Section
5
- class InvalidSnippet < StandardError
6
- def initialize(repo, marker)
7
- super "Error with marker #{marker.cyan} #{'in'.red} #{repo.cyan}#{'.'.red}"
8
- end
9
- end
10
-
11
- def get_snippet_and_language_at(marker)
12
- unless @repository.copied?
13
- @repository.announce_skip
14
- return ''
15
- end
16
-
17
- prepared_snippet_at(marker)
18
- end
19
-
20
- private
21
-
22
- def prepared_snippet_at(marker)
23
- snippet = ''
24
- FileUtils.cd(@repository.copied_to) { snippet = scrape_for(marker) }
25
-
26
- raise InvalidSnippet.new(full_name, marker) if snippet.empty?
27
- lines = snippet.split("\n")
28
- language_match = lines[0].match(/code_snippet #{Regexp.escape(marker)} start (\w+)/)
29
- language = language_match[1] if language_match
30
- [lines[1..-2].join("\n"), language]
31
- end
32
-
33
- def scrape_for(marker)
34
- locale = 'LC_CTYPE=C LANG=C' # Quiets 'sed: RE error: illegal byte sequence'
35
- result = `#{locale} find . -exec sed -ne '/code_snippet #{marker} start/,/code_snippet #{marker} end/ p' {} \\; 2> /dev/null`
36
- result = "" unless result.lines.last && result.lines.last.match(/code_snippet #{marker} end/)
37
- result
38
- end
39
- end
2
+ CodeExample = Struct.new(:path_to_repository,
3
+ :full_name,
4
+ :copied,
5
+ :destination_dir,
6
+ :directory_name)
40
7
  end
@@ -0,0 +1,40 @@
1
+ require_relative '../../lib/bookbinder/bookbinder_logger'
2
+
3
+ module Bookbinder
4
+ class CodeExampleReader
5
+ class InvalidSnippet < StandardError
6
+ def initialize(repo, marker)
7
+ super "Error with marker #{marker.cyan} #{'in'.red} #{repo.cyan}#{'.'.red}"
8
+ end
9
+ end
10
+
11
+ def initialize(logger)
12
+ @logger = logger
13
+ end
14
+
15
+ def get_snippet_and_language_at(marker, path_to_repository, copied, repo_name)
16
+ unless copied
17
+ logger.log ' skipping (not found) '.magenta + repo_name
18
+ return ''
19
+ end
20
+
21
+ snippet = ''
22
+ FileUtils.cd(path_to_repository) { locale = 'LC_CTYPE=C LANG=C' # Quiets 'sed: RE error: illegal byte sequence'
23
+ result = `#{locale} find . -exec sed -ne '/code_snippet #{marker} start/,/code_snippet #{marker} end/ p' {} \\; 2> /dev/null`
24
+ result = "" unless result.lines.last && result.lines.last.match(/code_snippet #{marker} end/)
25
+ scrape_for_value = result
26
+ snippet = scrape_for_value }
27
+
28
+ raise InvalidSnippet.new(repo_name, marker) if snippet.empty?
29
+ lines = snippet.split("\n")
30
+ language_match = lines[0].match(/code_snippet #{Regexp.escape(marker)} start (\w+)/)
31
+ language = language_match[1] if language_match
32
+ [lines[1..-2].join("\n"), language]
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :logger
38
+
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ require 'ansi/code'
2
+ require 'ostruct'
3
+
4
+ module Bookbinder
5
+
6
+ class Colorizer
7
+ Colors = OpenStruct.new(red: Proc.new { |msg| ANSI.red {msg} },
8
+ yellow: Proc.new { |msg| ANSI.yellow {msg} })
9
+
10
+ def colorize(string, color)
11
+ color.call string.to_s
12
+ end
13
+ end
14
+ end
@@ -1,32 +1,25 @@
1
1
  require_relative 'cli_error'
2
+ require_relative 'local_dita_to_html_converter'
3
+ require_relative 'sheller'
2
4
 
3
5
  module Bookbinder
4
6
  class CommandRunner
5
- def initialize(configuration_fetcher, usage_message, logger, commands)
6
- @configuration_fetcher = configuration_fetcher
7
- @usage_message = usage_message
7
+ def initialize(logger, commands)
8
8
  @logger = logger
9
9
  @commands = commands
10
10
  end
11
11
 
12
12
  def run(command_name, command_arguments)
13
- command = commands.detect { |known_command| known_command.command_name == command_name }
14
- begin
15
- if command_name == '--help'
16
- command.new(logger, usage_message).run command_arguments
17
- else
18
- command.new(logger, @configuration_fetcher).run command_arguments
19
- end
20
- rescue CliError::InvalidArguments
21
- logger.log command.usage
22
- 1
23
- end
13
+ command = commands.detect { |known_command| known_command.command_for?(command_name) }
14
+ command.run(command_arguments)
15
+ rescue CliError::InvalidArguments
16
+ logger.log command.usage
17
+ 1
24
18
  end
25
19
 
26
20
  private
27
21
 
28
- attr_reader :logger, :usage_message, :commands
29
-
22
+ attr_reader :logger, :commands
30
23
  end
31
24
  end
32
25
 
@@ -1,24 +1,31 @@
1
1
  require_relative 'cli_error'
2
+ require 'ostruct'
3
+ require_relative 'user_message'
2
4
 
3
5
  module Bookbinder
4
6
  class CommandValidator
5
- def initialize(usage_messenger, commands, usage_text)
6
- @usage_messenger = usage_messenger
7
+ def initialize(commands, usage_text)
7
8
  @commands = commands
8
9
  @usage_text = usage_text
9
10
  end
10
11
 
11
- def validate! command_name
12
- known_command_names = commands.map(&:command_name)
12
+ def validate command_name
13
13
  command_type = "#{command_name}".match(/^--/) ? 'flag' : 'command'
14
- if !known_command_names.include?(command_name)
15
- raise CliError::UnknownCommand.new "Unrecognized #{command_type} '#{command_name}'\n" + usage_text
14
+ if commands.none? { |command| command.command_for?(command_name) }
15
+ UserMessage.new "Unrecognized #{command_type} '#{command_name}'\n" + usage_text, EscalationType.error
16
+ elsif command = commands.find { |command| (command.respond_to? :deprecated_command_for?) &&
17
+ (command.deprecated_command_for? command_name) }
18
+ UserMessage.new "Use of #{command_name} is deprecated. " +
19
+ "The preferred usage is below: \n#{command.usage}",
20
+ EscalationType.warn
21
+ else
22
+ UserMessage.new "Success", EscalationType.success
16
23
  end
17
24
  end
18
25
 
19
26
  private
20
27
 
21
- attr_reader :usage_messenger, :commands, :usage_text
28
+ attr_reader :commands, :usage_text
22
29
  end
23
30
  end
24
31
 
@@ -0,0 +1,321 @@
1
+ require_relative '../archive_menu_configuration'
2
+ require_relative '../book'
3
+ require_relative '../cli_error'
4
+ require_relative '../directory_helpers'
5
+ require_relative '../publisher'
6
+ require_relative '../section'
7
+ require_relative '../dita_section'
8
+ require_relative '../dita_section_gatherer'
9
+ require_relative 'naming'
10
+
11
+ module Bookbinder
12
+ module Commands
13
+ class Bind
14
+ VersionUnsupportedError = Class.new(RuntimeError)
15
+
16
+ include Bookbinder::DirectoryHelperMethods
17
+ include Commands::Naming
18
+
19
+ def initialize(logger,
20
+ config_fetcher,
21
+ archive_menu_config,
22
+ version_control_system,
23
+ file_system_accessor,
24
+ static_site_generator,
25
+ sitemap_generator,
26
+ final_app_directory,
27
+ server_director,
28
+ context_dir,
29
+ dita_preprocessor)
30
+ @logger = logger
31
+ @config_fetcher = config_fetcher
32
+ @archive_menu_config = archive_menu_config
33
+ @version_control_system = version_control_system
34
+ @file_system_accessor = file_system_accessor
35
+ @static_site_generator = static_site_generator
36
+ @sitemap_generator = sitemap_generator
37
+ @final_app_directory = final_app_directory
38
+ @server_director = server_director
39
+ @context_dir = context_dir
40
+ @dita_preprocessor = dita_preprocessor
41
+ end
42
+
43
+ def usage
44
+ ["bind <local|github> [--verbose]",
45
+ "Bind the sections specified in config.yml from <local> or <github> into the final_app directory"]
46
+ end
47
+
48
+ def command_for?(test_command_name)
49
+ %w(bind publish).include?(test_command_name)
50
+ end
51
+
52
+ def deprecated_command_for?(command_name)
53
+ %w(publish).include?(command_name)
54
+ end
55
+
56
+ def run(cli_arguments)
57
+ raise CliError::InvalidArguments unless arguments_are_valid?(cli_arguments)
58
+ @section_repository = Repositories::SectionRepository.new(logger)
59
+ @gem_root = File.expand_path('../../../../', __FILE__)
60
+
61
+ @publisher = Publisher.new(logger, sitemap_generator, static_site_generator, server_director, file_system_accessor)
62
+
63
+ location = cli_arguments[0]
64
+
65
+ output_paths = output_directory_paths(location)
66
+ publish_config = publish_config(location)
67
+ @versions = publish_config.fetch(:versions, [])
68
+ @book_repo = publish_config[:book_repo]
69
+
70
+ master_middleman_dir = output_paths.fetch(:master_middleman_dir)
71
+ output_dir = output_paths.fetch(:output_dir)
72
+
73
+ dita_processing_dir = File.join output_dir, 'dita'
74
+ dita_section_dir = File.join dita_processing_dir, 'dita_sections'
75
+ dita_processed_dir = File.join dita_processing_dir, 'html_from_dita'
76
+ formatted_dir = File.join dita_processing_dir, 'site_generator_ready'
77
+
78
+ master_dir = File.join output_dir, 'master_middleman'
79
+ workspace_dir = File.join master_dir, 'source'
80
+ prepare_directories(final_app_directory,
81
+ output_dir,
82
+ workspace_dir,
83
+ master_middleman_dir,
84
+ master_dir,
85
+ dita_processing_dir,
86
+ formatted_dir)
87
+
88
+ dita_section_config_hash = config.dita_sections || {}
89
+ dita_sections = dita_section_config_hash.map do |dita_section_config|
90
+ relative_path_to_dita_map = dita_section_config['ditamap_location']
91
+ full_name = dita_section_config.fetch('repository', {}).fetch('name')
92
+ target_ref = dita_section_config.fetch('repository', {})['ref']
93
+ directory = dita_section_config['directory']
94
+
95
+ DitaSection.new(nil, relative_path_to_dita_map, full_name, target_ref, directory)
96
+ end
97
+
98
+ if location == 'github'
99
+ dita_section_gatherer = DitaSectionGatherer.new(version_control_system, logger)
100
+ final_dita_sections = dita_section_gatherer.gather(dita_sections, to: dita_section_dir)
101
+ else
102
+ final_dita_sections = dita_sections.map do |dita_section|
103
+ relative_path_to_dita_map = dita_section.ditamap_location
104
+ full_name = dita_section.full_name
105
+ target_ref = dita_section.target_ref
106
+ directory = dita_section.directory
107
+
108
+ path_to_local_copy = File.join output_paths[:local_repo_dir], directory
109
+
110
+ DitaSection.new(path_to_local_copy, relative_path_to_dita_map, full_name, target_ref, directory)
111
+ end
112
+ end
113
+
114
+ dita_preprocessor.preprocess final_dita_sections, dita_processed_dir, formatted_dir, workspace_dir
115
+
116
+ sections = gather_sections(workspace_dir, output_paths)
117
+
118
+ subnavs = subnavs_by_dir_name(sections)
119
+
120
+ success = publisher.publish(
121
+ subnavs,
122
+ {verbose: cli_arguments.include?('--verbose')},
123
+ output_paths,
124
+ archive_menu_config.generate(publish_config, sections)
125
+ )
126
+
127
+ success ? 0 : 1
128
+ end
129
+
130
+ private
131
+
132
+ attr_reader :publisher,
133
+ :version_control_system,
134
+ :config_fetcher,
135
+ :archive_menu_config,
136
+ :logger,
137
+ :file_system_accessor,
138
+ :static_site_generator,
139
+ :final_app_directory,
140
+ :sitemap_generator,
141
+ :server_director,
142
+ :context_dir,
143
+ :dita_preprocessor
144
+
145
+ def gather_sections(workspace, output_paths)
146
+ config.sections.map do |attributes|
147
+
148
+ local_repo_dir = output_paths[:local_repo_dir]
149
+ vcs_repo =
150
+ if local_repo_dir
151
+ GitHubRepository.
152
+ build_from_local(logger, attributes, local_repo_dir, version_control_system).
153
+ tap { |repo| repo.copy_from_local(workspace) }
154
+ else
155
+ GitHubRepository.
156
+ build_from_remote(logger, attributes, nil, version_control_system).
157
+ tap { |repo| repo.copy_from_remote(workspace) }
158
+ end
159
+
160
+ @section_repository.get_instance(attributes,
161
+ vcs_repo: vcs_repo,
162
+ destination_dir: workspace,
163
+ build: ->(*args) { Section.new(*args) })
164
+ end
165
+ end
166
+
167
+ def prepare_directories(final_app,
168
+ output_dir,
169
+ middleman_source,
170
+ master_middleman_dir,
171
+ middleman_dir,
172
+ dita_processing_dir,
173
+ formatted_dir)
174
+ forget_sections(output_dir)
175
+ file_system_accessor.remove_directory File.join final_app, '.'
176
+ file_system_accessor.remove_directory dita_processing_dir
177
+ file_system_accessor.make_directory output_dir
178
+ file_system_accessor.make_directory formatted_dir
179
+ file_system_accessor.make_directory File.join dita_processing_dir, 'dita_sections'
180
+ file_system_accessor.make_directory File.join final_app, 'public'
181
+ file_system_accessor.make_directory middleman_source
182
+
183
+ copy_directory_from_gem 'template_app', final_app
184
+ copy_directory_from_gem 'master_middleman', middleman_dir
185
+ file_system_accessor.copy File.join(master_middleman_dir, '.'), middleman_dir
186
+
187
+ copy_version_master_middleman(middleman_source)
188
+ end
189
+
190
+ def forget_sections(middleman_scratch)
191
+ file_system_accessor.remove_directory File.join middleman_scratch, '.'
192
+ end
193
+
194
+ def copy_directory_from_gem(dir, output_dir)
195
+ file_system_accessor.copy File.join(@gem_root, "#{dir}/."), output_dir
196
+ end
197
+
198
+ # Copy the index file from each version into the version's directory. Because version
199
+ # subdirectories are sections, this is the only way they get content from their master
200
+ # middleman directory.
201
+ def copy_version_master_middleman(dest_dir)
202
+ @versions.each do |version|
203
+ Dir.mktmpdir(version) do |tmpdir|
204
+ book = Book.from_remote(logger: logger,
205
+ full_name: @book_repo,
206
+ destination_dir: tmpdir,
207
+ ref: version,
208
+ git_accessor: version_control_system)
209
+ index_source_dir = File.join(tmpdir, book.directory, 'master_middleman', source_dir_name)
210
+ index_dest_dir = File.join(dest_dir, version)
211
+ file_system_accessor.make_directory(index_dest_dir)
212
+
213
+ Dir.glob(File.join(index_source_dir, 'index.*')) do |f|
214
+ file_system_accessor.copy(File.expand_path(f), index_dest_dir)
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def output_directory_paths(location)
221
+ local_repo_dir = (location == 'local') ? File.expand_path('..', context_dir) : nil
222
+
223
+ {
224
+ final_app_dir: final_app_directory,
225
+ local_repo_dir: local_repo_dir,
226
+ output_dir: File.join(context_dir, output_dir_name),
227
+ master_middleman_dir: layout_repo_path(local_repo_dir)
228
+ }
229
+ end
230
+
231
+ def publish_config(location)
232
+ arguments = {
233
+ sections: config.sections,
234
+ book_repo: config.book_repo,
235
+ host_for_sitemap: config.public_host,
236
+ archive_menu: config.archive_menu
237
+ }
238
+
239
+ optional_arguments = {}
240
+ optional_arguments.merge!(template_variables: config.template_variables) if config.respond_to?(:template_variables)
241
+ if publishing_to_github? location
242
+ config.versions.each { |version| arguments[:sections].concat sections_from version }
243
+ optional_arguments.merge!(versions: config.versions)
244
+ end
245
+
246
+ arguments.merge! optional_arguments
247
+ end
248
+
249
+ def config
250
+ config_fetcher.fetch_config
251
+ end
252
+
253
+ def sections_from(version)
254
+ Dir.mktmpdir('book_checkout') do |temp_workspace|
255
+ book = Book.from_remote(logger: logger,
256
+ full_name: config.book_repo,
257
+ destination_dir: temp_workspace,
258
+ ref: version,
259
+ git_accessor: version_control_system,
260
+ )
261
+
262
+ book_checkout_value = File.join temp_workspace, book.directory
263
+ config_file = File.join book_checkout_value, 'config.yml'
264
+ attrs = YAML.load(File.read(config_file))['sections']
265
+ raise VersionUnsupportedError.new(version) if attrs.nil?
266
+
267
+ attrs.map do |section_hash|
268
+ section_hash['repository']['ref'] = version
269
+ section_hash['directory'] = File.join(version, section_hash['directory'])
270
+ section_hash
271
+ end
272
+ end
273
+ end
274
+
275
+ def layout_repo_path(local_repo_dir)
276
+ if config.has_option?('layout_repo')
277
+ if local_repo_dir
278
+ File.join(local_repo_dir, config.layout_repo.split('/').last)
279
+ else
280
+ section = {'repository' => {'name' => config.layout_repo}}
281
+ destination_dir = Dir.mktmpdir
282
+ repository = GitHubRepository.build_from_remote(logger,
283
+ section,
284
+ 'master',
285
+ version_control_system)
286
+ repository.copy_from_remote(destination_dir)
287
+ if repository
288
+ File.join(destination_dir, repository.directory)
289
+ else
290
+ raise 'failed to fetch repository'
291
+ end
292
+ end
293
+ else
294
+ File.absolute_path('master_middleman')
295
+ end
296
+ end
297
+
298
+ def arguments_are_valid?(arguments)
299
+ return false unless arguments.any?
300
+ verbose = arguments[1] && arguments[1..-1].include?('--verbose')
301
+ tag_provided = arguments[1] && (arguments[1..-1] - ['--verbose']).any?
302
+ nothing_special = arguments[1..-1].empty?
303
+
304
+ %w(local github).include?(arguments[0]) && (tag_provided || verbose || nothing_special)
305
+ end
306
+
307
+ def publishing_to_github?(publish_location)
308
+ config.has_option?('versions') && publish_location != 'local'
309
+ end
310
+
311
+ def subnavs_by_dir_name(sections)
312
+ sections.reduce({}) do |subnavs, section|
313
+ namespace = section.directory.gsub('/', '_')
314
+ template = section.subnav_template || 'default'
315
+
316
+ subnavs.merge(namespace => template)
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end