bookwatch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/bookwatch.gemspec +40 -0
  3. data/install_bin/bookwatch +5 -0
  4. data/lib/bookwatch/cli.rb +109 -0
  5. data/lib/bookwatch/code_example_reader.rb +95 -0
  6. data/lib/bookwatch/colorizer.rb +16 -0
  7. data/lib/bookwatch/commands/bind.rb +119 -0
  8. data/lib/bookwatch/commands/collection.rb +181 -0
  9. data/lib/bookwatch/commands/components/bind/directory_preparer.rb +33 -0
  10. data/lib/bookwatch/commands/components/bind/layout_preparer.rb +27 -0
  11. data/lib/bookwatch/commands/components/command_options.rb +45 -0
  12. data/lib/bookwatch/commands/components/imprint/directory_preparer.rb +24 -0
  13. data/lib/bookwatch/commands/generate.rb +92 -0
  14. data/lib/bookwatch/commands/imprint.rb +55 -0
  15. data/lib/bookwatch/commands/punch.rb +33 -0
  16. data/lib/bookwatch/commands/update_local_doc_repos.rb +42 -0
  17. data/lib/bookwatch/commands/watch.rb +100 -0
  18. data/lib/bookwatch/config/checkers/archive_menu_checker.rb +29 -0
  19. data/lib/bookwatch/config/checkers/ditamap_presence_checker.rb +27 -0
  20. data/lib/bookwatch/config/checkers/duplicate_section_name_checker.rb +33 -0
  21. data/lib/bookwatch/config/checkers/products_checker.rb +34 -0
  22. data/lib/bookwatch/config/checkers/repository_name_presence_checker.rb +34 -0
  23. data/lib/bookwatch/config/checkers/required_keys_checker.rb +18 -0
  24. data/lib/bookwatch/config/checkers/section_presence_checker.rb +15 -0
  25. data/lib/bookwatch/config/configuration.rb +119 -0
  26. data/lib/bookwatch/config/configuration_decorator.rb +54 -0
  27. data/lib/bookwatch/config/dita_config_generator.rb +61 -0
  28. data/lib/bookwatch/config/fetcher.rb +64 -0
  29. data/lib/bookwatch/config/imprint/configuration.rb +24 -0
  30. data/lib/bookwatch/config/product_config.rb +34 -0
  31. data/lib/bookwatch/config/section_config.rb +85 -0
  32. data/lib/bookwatch/config/validator.rb +33 -0
  33. data/lib/bookwatch/config/yaml_loader.rb +34 -0
  34. data/lib/bookwatch/css_link_checker.rb +67 -0
  35. data/lib/bookwatch/directory_helpers.rb +15 -0
  36. data/lib/bookwatch/dita_command_creator.rb +95 -0
  37. data/lib/bookwatch/dita_html_for_middleman_formatter.rb +45 -0
  38. data/lib/bookwatch/errors/programmer_mistake.rb +5 -0
  39. data/lib/bookwatch/html_document_manipulator.rb +21 -0
  40. data/lib/bookwatch/ingest/cloner_factory.rb +26 -0
  41. data/lib/bookwatch/ingest/destination_directory.rb +21 -0
  42. data/lib/bookwatch/ingest/git_accessor.rb +102 -0
  43. data/lib/bookwatch/ingest/git_cloner.rb +36 -0
  44. data/lib/bookwatch/ingest/local_filesystem_cloner.rb +66 -0
  45. data/lib/bookwatch/ingest/missing_working_copy.rb +27 -0
  46. data/lib/bookwatch/ingest/repo_identifier.rb +45 -0
  47. data/lib/bookwatch/ingest/section_repository.rb +49 -0
  48. data/lib/bookwatch/ingest/update_failure.rb +15 -0
  49. data/lib/bookwatch/ingest/update_success.rb +12 -0
  50. data/lib/bookwatch/ingest/working_copy.rb +36 -0
  51. data/lib/bookwatch/local_filesystem_accessor.rb +122 -0
  52. data/lib/bookwatch/middleman_runner.rb +48 -0
  53. data/lib/bookwatch/postprocessing/link_checker.rb +125 -0
  54. data/lib/bookwatch/postprocessing/redirection.rb +38 -0
  55. data/lib/bookwatch/preprocessing/dita_html_preprocessor.rb +91 -0
  56. data/lib/bookwatch/preprocessing/dita_pdf_preprocessor.rb +48 -0
  57. data/lib/bookwatch/preprocessing/link_to_site_gen_dir.rb +39 -0
  58. data/lib/bookwatch/preprocessing/preprocessor.rb +26 -0
  59. data/lib/bookwatch/server_director.rb +30 -0
  60. data/lib/bookwatch/sheller.rb +52 -0
  61. data/lib/bookwatch/streams/colorized_stream.rb +25 -0
  62. data/lib/bookwatch/streams/filter_stream.rb +22 -0
  63. data/lib/bookwatch/subnav/navigation_entries_from_html_toc.rb +59 -0
  64. data/lib/bookwatch/subnav/navigation_entries_from_markdown_root.rb +116 -0
  65. data/lib/bookwatch/subnav/pdf_config_creator.rb +50 -0
  66. data/lib/bookwatch/subnav/subnav_generator.rb +28 -0
  67. data/lib/bookwatch/subnav/subnav_generator_factory.rb +29 -0
  68. data/lib/bookwatch/subnav/template_creator.rb +71 -0
  69. data/lib/bookwatch/terminal.rb +19 -0
  70. data/lib/bookwatch/values/output_locations.rb +91 -0
  71. data/lib/bookwatch/values/product_info.rb +11 -0
  72. data/lib/bookwatch/values/section.rb +58 -0
  73. data/lib/bookwatch/values/subnav_template.rb +4 -0
  74. data/lib/bookwatch/values/user_message.rb +15 -0
  75. data/master_middleman/archive_drop_down_menu.rb +50 -0
  76. data/master_middleman/bookwatch_helpers.rb +259 -0
  77. data/master_middleman/compass_runner.rb +0 -0
  78. data/master_middleman/config.rb +34 -0
  79. data/master_middleman/quicklinks_renderer.rb +80 -0
  80. data/master_middleman/source/javascripts/all.js +2 -0
  81. data/master_middleman/source/javascripts/book.js +1 -0
  82. data/master_middleman/source/javascripts/bookwatch.js +103 -0
  83. data/master_middleman/source/layouts/_additional-scripts.erb +0 -0
  84. data/master_middleman/source/layouts/_book-footer.erb +0 -0
  85. data/master_middleman/source/layouts/_book-search.erb +0 -0
  86. data/master_middleman/source/layouts/_book-title.erb +3 -0
  87. data/master_middleman/source/layouts/_header.erb +34 -0
  88. data/master_middleman/source/layouts/_local-header.erb +0 -0
  89. data/master_middleman/source/layouts/_page-footer.erb +1 -0
  90. data/master_middleman/source/layouts/_title.erb +5 -0
  91. data/master_middleman/source/layouts/layout.erb +69 -0
  92. data/master_middleman/source/stylesheets/all.css.scss +3 -0
  93. data/master_middleman/source/stylesheets/base.scss +380 -0
  94. data/master_middleman/source/stylesheets/book-styles.css.scss +0 -0
  95. data/master_middleman/source/stylesheets/layout-styles.scss +0 -0
  96. data/master_middleman/source/stylesheets/partials/_book-base-values.scss +0 -0
  97. data/master_middleman/source/stylesheets/partials/_book-vars.scss +0 -0
  98. data/master_middleman/source/stylesheets/partials/_default.scss +300 -0
  99. data/master_middleman/source/stylesheets/partials/_footer.scss +64 -0
  100. data/master_middleman/source/stylesheets/partials/_header.scss +419 -0
  101. data/master_middleman/source/stylesheets/partials/_layout-vars.scss +0 -0
  102. data/master_middleman/source/stylesheets/partials/_mixins.scss +53 -0
  103. data/master_middleman/source/stylesheets/partials/_reset.scss +233 -0
  104. data/master_middleman/source/stylesheets/partials/_search.scss +78 -0
  105. data/master_middleman/source/stylesheets/partials/_sidenav.scss +191 -0
  106. data/master_middleman/source/stylesheets/partials/_syntax-highlight.scss +64 -0
  107. data/master_middleman/source/stylesheets/partials/_vars.scss +64 -0
  108. data/master_middleman/source/stylesheets/print.css.scss +58 -0
  109. data/master_middleman/source/subnavs/_default.erb +0 -0
  110. data/master_middleman/source/subnavs/_nav-links.erb +10 -0
  111. data/master_middleman/source/subnavs/_subnav_template.erb +8 -0
  112. data/master_middleman/subdirectory_aware_assets.rb +47 -0
  113. data/template_app/Gemfile +10 -0
  114. data/template_app/Gemfile.lock +43 -0
  115. data/template_app/config.ru +9 -0
  116. data/template_app/lib/rack_static_if_exists.rb +19 -0
  117. data/template_app/lib/search/handler.rb +47 -0
  118. data/template_app/lib/search/hit.rb +21 -0
  119. data/template_app/lib/search/query.rb +74 -0
  120. data/template_app/lib/search/renderer.rb +29 -0
  121. data/template_app/lib/server.rb +52 -0
  122. data/template_app/mail_sender.rb +69 -0
  123. data/template_app/rack_app.rb +110 -0
  124. data/template_app/search-results.html.erb +75 -0
  125. data/template_app/search.yml +22 -0
  126. metadata +491 -0
@@ -0,0 +1,21 @@
1
+ require 'nokogiri'
2
+
3
+ module Bookwatch
4
+ class HtmlDocumentManipulator
5
+ def set_attribute(document: nil,
6
+ selector: nil,
7
+ attribute: nil,
8
+ value: nil)
9
+ doc = Nokogiri::HTML.fragment(document)
10
+ node_set = doc.css(selector)
11
+ node_set.attr(attribute, value)
12
+ doc.to_html
13
+ end
14
+
15
+ def read_html_in_tag(document: nil, tag: nil)
16
+ doc = Nokogiri::HTML(document)
17
+ doc.css(tag).inner_html
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,26 @@
1
+ require_relative 'git_cloner'
2
+ require_relative 'local_filesystem_cloner'
3
+
4
+ module Bookwatch
5
+ module Ingest
6
+ class ClonerFactory
7
+ def initialize(streams, filesystem, version_control_system)
8
+ @streams = streams
9
+ @filesystem = filesystem
10
+ @version_control_system = version_control_system
11
+ end
12
+
13
+ def produce(user_repo_dir)
14
+ if user_repo_dir
15
+ LocalFilesystemCloner.new(streams, filesystem, user_repo_dir)
16
+ else
17
+ GitCloner.new(version_control_system)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :streams, :filesystem, :version_control_system
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ module Bookwatch
2
+ module Ingest
3
+ DestinationDirectory = Struct.new(:full_repo_name, :desired_destination_dir_name) do
4
+ def to_str
5
+ if desired_destination_dir_name
6
+ desired_destination_dir_name.to_s
7
+ elsif full_repo_name
8
+ full_repo_name.split('/').last
9
+ else
10
+ ""
11
+ end
12
+ end
13
+
14
+ alias :to_s :to_str
15
+
16
+ def ==(other)
17
+ to_str == other
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,102 @@
1
+ require 'git'
2
+ require_relative '../directory_helpers'
3
+ require_relative '../local_filesystem_accessor'
4
+ require_relative 'update_failure'
5
+ require_relative 'update_success'
6
+
7
+ module Bookwatch
8
+ module Ingest
9
+ class GitAccessor
10
+ TagExists = Class.new(RuntimeError)
11
+ InvalidTagRef = Class.new(RuntimeError)
12
+ include DirectoryHelperMethods
13
+
14
+ def clone(url, name, path: nil, checkout: 'master')
15
+ cached_clone(url, name, Pathname(path)).tap do |git|
16
+ git.checkout(checkout)
17
+ end
18
+ end
19
+
20
+ def update(cloned_path)
21
+ Git.open(cloned_path).pull
22
+ Ingest::UpdateSuccess.new
23
+ rescue ArgumentError, Git::GitExecuteError => e
24
+ case e.message
25
+ when /overwritten by merge/
26
+ Ingest::UpdateFailure.new('merge error')
27
+ when /path does not exist/
28
+ Ingest::UpdateFailure.new('not found')
29
+ else
30
+ raise
31
+ end
32
+ end
33
+
34
+ def remote_tag(url, tagname, commit_or_object)
35
+ Dir.mktmpdir do |dir|
36
+ path = Pathname(dir)
37
+ git = cached_clone(url, temp_name("tag"), path)
38
+ git.config('user.name', 'Bookwatch')
39
+ git.config('user.email', 'bookwatch@cloudfoundry.org')
40
+ begin
41
+ git.add_tag(tagname, "origin/#{commit_or_object}",
42
+ message: 'Tagged by Bookwatch')
43
+ rescue Git::GitExecuteError => e
44
+ case e.message
45
+ when /already exists/
46
+ raise TagExists
47
+ when /as a valid ref/
48
+ raise InvalidTagRef
49
+ else
50
+ raise
51
+ end
52
+ end
53
+ git.push("origin", "master", tags: true)
54
+ end
55
+ end
56
+
57
+ def author_date(path, exclusion_flag: '[exclude]', dita: false)
58
+ fs = LocalFilesystemAccessor.new
59
+
60
+ if dita
61
+ source_dir = 'preprocessing'
62
+ path_to_file = path.sub(/\.html(.md)?(.erb)?/, '.xml')
63
+ else
64
+ source_dir = source_dir_name
65
+ path_to_file = path
66
+ end
67
+
68
+
69
+ Pathname(path).dirname.ascend do |current_dir|
70
+ if (
71
+ current_dir.to_s.include?(source_dir) &&
72
+ current_dir.entries.include?(Pathname(".git")) &&
73
+ fs.source_file_exists?(Pathname(path).dirname, path_to_file)
74
+ )
75
+
76
+ git = Git.open(current_dir)
77
+ logs = git.gblob(path_to_file).log
78
+
79
+ last_non_excluded_commit = logs.detect { |log| !log.message.include?(exclusion_flag) }
80
+
81
+ return last_non_excluded_commit.author.date if last_non_excluded_commit
82
+ end
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def temp_name(purpose)
89
+ "bookwatch-git-accessor-#{purpose}"
90
+ end
91
+
92
+ def cached_clone(url, name, path)
93
+ dest_dir = path.join(name)
94
+ if dest_dir.exist?
95
+ Git.open(dest_dir)
96
+ else
97
+ Git.clone(url, name, path: path, recursive: true)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'destination_directory'
2
+ require_relative 'repo_identifier'
3
+ require_relative 'working_copy'
4
+
5
+ module Bookwatch
6
+ module Ingest
7
+ class GitCloner
8
+ def initialize(version_control_system)
9
+ @version_control_system = version_control_system
10
+ end
11
+
12
+ def call(source_repo_name: nil,
13
+ source_ref: "master",
14
+ destination_parent_dir: nil,
15
+ destination_dir_name: nil)
16
+ dest_dir = DestinationDirectory.new(source_repo_name, destination_dir_name)
17
+ copied_to = Pathname(destination_parent_dir).join(dest_dir)
18
+ version_control_system.clone(
19
+ RepoIdentifier.new(source_repo_name),
20
+ dest_dir,
21
+ path: destination_parent_dir,
22
+ checkout: source_ref
23
+ )
24
+ WorkingCopy.new(
25
+ copied_to: copied_to,
26
+ full_name: source_repo_name,
27
+ ref: source_ref
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :version_control_system
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,66 @@
1
+ require_relative 'destination_directory'
2
+ require_relative 'working_copy'
3
+ require_relative 'missing_working_copy'
4
+
5
+ module Bookwatch
6
+ module Ingest
7
+ class LocalFilesystemCloner
8
+ def initialize(streams, filesystem, user_repo_dir)
9
+ @streams = streams
10
+ @user_repo_dir = user_repo_dir
11
+ @filesystem = filesystem
12
+ end
13
+
14
+ def call(source_repo_name: nil,
15
+ source_ref: nil,
16
+ destination_parent_dir: nil,
17
+ destination_dir_name: nil)
18
+ link!(
19
+ source_repo_name,
20
+ Pathname(user_repo_dir).join(source_repo_name.split('/').last),
21
+ Pathname(destination_parent_dir).join(DestinationDirectory.new(source_repo_name, destination_dir_name)),
22
+ source_ref,
23
+ source_repo_name.split('/').first
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :streams, :filesystem, :user_repo_dir
30
+
31
+ def link!(source_repo_name, source_dir, dest_dir, source_ref, source_org)
32
+ source_exists = filesystem.file_exist?(source_dir)
33
+ unless source_exists
34
+ source_dir_with_ref = "#{source_dir}-#{source_ref}"
35
+ source_exists = filesystem.file_exist?(source_dir_with_ref)
36
+
37
+ if source_exists
38
+ source_dir = source_dir_with_ref
39
+ else
40
+ source_dir_with_org_and_ref = "#{source_dir}-#{source_org}-#{source_ref}"
41
+ source_exists = filesystem.file_exist?(source_dir_with_org_and_ref)
42
+
43
+ source_dir = source_dir_with_org_and_ref if source_exists
44
+ end
45
+ end
46
+
47
+ if !source_exists
48
+ streams[:out].puts " skipping (not found) #{source_dir}"
49
+ MissingWorkingCopy.new(source_repo_name, source_dir)
50
+ else
51
+ streams[:out].puts " copying #{source_dir}"
52
+
53
+ unless filesystem.file_exist?(dest_dir)
54
+ filesystem.link_creating_intermediate_dirs(source_dir, dest_dir)
55
+ end
56
+
57
+ WorkingCopy.new(
58
+ copied_to: dest_dir,
59
+ full_name: source_repo_name,
60
+ ref: source_ref
61
+ )
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ module Bookwatch
2
+ module Ingest
3
+ class MissingWorkingCopy
4
+ def initialize(source_repo_name, source_dir, source_ref=nil)
5
+ @source_repo_name = source_repo_name
6
+ @source_dir = source_dir
7
+ @source_ref = source_ref
8
+ end
9
+
10
+ def full_name
11
+ @source_repo_name
12
+ end
13
+
14
+ def path
15
+ Pathname(@source_dir)
16
+ end
17
+
18
+ def ref
19
+ @source_ref
20
+ end
21
+
22
+ def available?
23
+ false
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ module Bookwatch
2
+ module Ingest
3
+ class RepoIdentifier
4
+ DEFAULT_VCS_PREFIX = 'git@github.com:'
5
+
6
+ def initialize(input_identifier)
7
+ @input_identifier = input_identifier
8
+ end
9
+
10
+ def to_str
11
+ if input_identifier.nil?
12
+ ""
13
+ elsif input_identifier.include?(':') || input_identifier.match(/^\//)
14
+ input_identifier
15
+ else
16
+ "#{DEFAULT_VCS_PREFIX}#{input_identifier}"
17
+ end
18
+ end
19
+
20
+ alias :to_s :to_str
21
+
22
+ def inspect
23
+ %Q("#{to_s}")
24
+ end
25
+
26
+ def split(*args)
27
+ input_identifier.split(*args)
28
+ end
29
+
30
+ def hash
31
+ to_str.hash
32
+ end
33
+
34
+ def ==(other)
35
+ to_str == other
36
+ end
37
+
38
+ alias :eql? :==
39
+
40
+ private
41
+
42
+ attr_reader :input_identifier
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ require_relative '../values/section'
2
+ require_relative '../values/product_info'
3
+
4
+ module Bookwatch
5
+ module Ingest
6
+ class SectionRepository
7
+ def fetch(configured_sections: [],
8
+ destination_dir: nil,
9
+ ref_override: nil,
10
+ cloner: nil,
11
+ streams: nil)
12
+ configured_sections.map do |section_config|
13
+ streams[:success].puts("Gathering #{section_config.repo_name}")
14
+
15
+ working_copy = cloner.call(source_repo_name: section_config.repo_name,
16
+ source_ref: ref_override || section_config.repo_ref,
17
+ destination_parent_dir: destination_dir,
18
+ destination_dir_name: section_config.desired_directory_name)
19
+
20
+ if section_config.dependent_sections.any?
21
+ section_config.dependent_sections.map do |dependent_config|
22
+ unless dependent_config.no_docs?
23
+ streams[:success].puts("Gathering #{dependent_config.repo_name}")
24
+ cloner.call(source_repo_name: dependent_config.repo_name,
25
+ source_ref: ref_override || dependent_config.repo_ref,
26
+ destination_parent_dir: "#{destination_dir}/#{section_config.desired_directory_name}",
27
+ destination_dir_name: dependent_config.desired_directory_name)
28
+ end
29
+ end
30
+ end
31
+
32
+ Section.new(
33
+ working_copy.path,
34
+ working_copy.full_name,
35
+ section_config.desired_directory_name,
36
+ section_config.subnav_template,
37
+ section_config.subnav_name,
38
+ section_config.preprocessor_config,
39
+ section_config.at_repo_path,
40
+ section_config.repo_name,
41
+ working_copy.ref,
42
+ section_config.pdf_output_filename,
43
+ section_config.product_info
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ module Bookwatch
2
+ module Ingest
3
+ class UpdateFailure
4
+ attr_reader :reason
5
+
6
+ def initialize(reason)
7
+ @reason = reason
8
+ end
9
+
10
+ def success?
11
+ false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module Bookwatch
2
+ module Ingest
3
+ class UpdateSuccess
4
+ def reason
5
+ end
6
+
7
+ def success?
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ require_relative '../errors/programmer_mistake'
2
+
3
+ module Bookwatch
4
+ module Ingest
5
+ class WorkingCopy
6
+ attr_reader :full_name, :ref
7
+
8
+ def initialize(copied_to: nil,
9
+ full_name: nil,
10
+ ref: nil)
11
+ if [copied_to, full_name].none?
12
+ raise Errors::ProgrammerMistake.new("Must provide copied_to and/or full_name to WorkingCopy.new")
13
+ else
14
+ @copied_to = copied_to
15
+ @full_name = full_name
16
+ @ref = ref
17
+ end
18
+ end
19
+
20
+ def available?
21
+ !! @copied_to
22
+ end
23
+
24
+ def path
25
+ Pathname(@copied_to)
26
+ end
27
+
28
+ def ==(other)
29
+ [@copied_to, @full_name, @ref] ==
30
+ [other.instance_variable_get(:@copied_to),
31
+ other.instance_variable_get(:@full_name),
32
+ other.instance_variable_get(:@ref)]
33
+ end
34
+ end
35
+ end
36
+ end