giblish 0.8.2 → 2.0.0.pre.alpha1

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/unit_tests.yml +30 -0
  3. data/.gitignore +7 -3
  4. data/.ruby-version +1 -1
  5. data/Changelog.adoc +59 -0
  6. data/README.adoc +261 -0
  7. data/docs/concepts/text_search.adoc +213 -0
  8. data/docs/concepts/text_search_im/cgi-search_request.puml +35 -0
  9. data/docs/concepts/text_search_im/cgi-search_request.svg +397 -0
  10. data/docs/concepts/text_search_im/search_request.puml +40 -0
  11. data/docs/concepts/text_search_im/search_request.svg +408 -0
  12. data/docs/howtos/trigger_generation.adoc +180 -0
  13. data/docs/{setup_server_assets → howtos/trigger_generation_im}/Render Documents.png +0 -0
  14. data/docs/{setup_server_assets → howtos/trigger_generation_im}/View Documents.png +0 -0
  15. data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.graphml +0 -0
  16. data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.svg +0 -0
  17. data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.graphml +0 -0
  18. data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.svg +0 -0
  19. data/docs/howtos/trigger_generation_im/docgen_github.puml +51 -0
  20. data/docs/{setup_server_assets → howtos/trigger_generation_im}/giblish_deployment.graphml +0 -0
  21. data/docs/howtos/trigger_generation_im/post-receive-example.sh +50 -0
  22. data/docs/reference/box_flow_spec.adoc +22 -0
  23. data/docs/reference/search_spec.adoc +185 -0
  24. data/giblish.gemspec +54 -32
  25. data/lib/giblish/adocsrc_providers.rb +23 -0
  26. data/lib/giblish/application.rb +214 -41
  27. data/lib/giblish/cmdline.rb +273 -259
  28. data/lib/giblish/config_utils.rb +41 -0
  29. data/lib/giblish/configurator.rb +163 -0
  30. data/lib/giblish/conversion_info.rb +120 -0
  31. data/lib/giblish/docattr_providers.rb +125 -0
  32. data/lib/giblish/docid/docid.rb +181 -0
  33. data/lib/giblish/github_trigger/webhook_manager.rb +64 -0
  34. data/lib/giblish/gitrepos/checkoutmanager.rb +124 -0
  35. data/lib/giblish/{gititf.rb → gitrepos/gititf.rb} +30 -4
  36. data/lib/giblish/gitrepos/gitsummary.erb +61 -0
  37. data/lib/giblish/gitrepos/gitsummaryprovider.rb +78 -0
  38. data/lib/giblish/gitrepos/history_pb.rb +41 -0
  39. data/lib/giblish/indexbuilders/d3treegraph.rb +88 -0
  40. data/lib/giblish/indexbuilders/depgraphbuilder.rb +109 -0
  41. data/lib/giblish/indexbuilders/dotdigraphadoc.rb +174 -0
  42. data/lib/giblish/indexbuilders/standard_index.erb +10 -0
  43. data/lib/giblish/indexbuilders/subtree_indices.rb +132 -0
  44. data/lib/giblish/indexbuilders/templates/circles.html.erb +111 -0
  45. data/lib/giblish/indexbuilders/templates/flame.html.erb +61 -0
  46. data/lib/giblish/indexbuilders/templates/tree.html.erb +366 -0
  47. data/lib/giblish/indexbuilders/templates/treemap.html.erb +127 -0
  48. data/lib/giblish/indexbuilders/verbatimtree.rb +94 -0
  49. data/lib/giblish/pathtree.rb +473 -74
  50. data/lib/giblish/resourcepaths.rb +150 -0
  51. data/lib/giblish/search/expand_adoc.rb +55 -0
  52. data/lib/giblish/search/headingindexer.rb +312 -0
  53. data/lib/giblish/search/request_manager.rb +110 -0
  54. data/lib/giblish/search/searchquery.rb +68 -0
  55. data/lib/giblish/search/textsearcher.rb +349 -0
  56. data/lib/giblish/subtreeinfobuilder.rb +77 -0
  57. data/lib/giblish/treeconverter.rb +272 -0
  58. data/lib/giblish/utils.rb +142 -294
  59. data/lib/giblish/version.rb +1 -1
  60. data/lib/giblish.rb +10 -7
  61. data/scripts/hooks/post-receive.example +66 -0
  62. data/{docgen/scripts/githook_examples → scripts/hooks}/post-update.example +0 -0
  63. data/{docgen → scripts}/resources/css/adoc-colony.css +0 -0
  64. data/scripts/resources/css/giblish-serif.css +419 -0
  65. data/scripts/resources/css/giblish.css +1979 -419
  66. data/{docgen → scripts}/resources/fonts/Ubuntu-B.ttf +0 -0
  67. data/{docgen → scripts}/resources/fonts/Ubuntu-BI.ttf +0 -0
  68. data/{docgen → scripts}/resources/fonts/Ubuntu-R.ttf +0 -0
  69. data/{docgen → scripts}/resources/fonts/Ubuntu-RI.ttf +0 -0
  70. data/{docgen → scripts}/resources/fonts/mplus1p-regular-fallback.ttf +0 -0
  71. data/{docgen → scripts}/resources/images/giblish_logo.png +0 -0
  72. data/{docgen → scripts}/resources/images/giblish_logo.svg +0 -0
  73. data/{docgen → scripts}/resources/themes/giblish.yml +0 -0
  74. data/scripts/wserv_development.rb +32 -0
  75. data/web_apps/cgi_search/gibsearch.rb +43 -0
  76. data/web_apps/gh_webhook_trigger/config.ru +2 -0
  77. data/web_apps/gh_webhook_trigger/gh_webhook_trigger.rb +73 -0
  78. data/web_apps/gh_webhook_trigger/public/dummy.txt +3 -0
  79. data/web_apps/sinatra_search/config.ru +2 -0
  80. data/web_apps/sinatra_search/public/dummy.txt +3 -0
  81. data/web_apps/sinatra_search/sinatra_search.rb +34 -0
  82. data/web_apps/sinatra_search/tmp/restart.txt +0 -0
  83. metadata +188 -85
  84. data/.rubocop.yml +0 -7
  85. data/.travis.yml +0 -3
  86. data/Changelog +0 -16
  87. data/Gemfile +0 -4
  88. data/README.adoc +0 -1
  89. data/Rakefile +0 -41
  90. data/bin/console +0 -14
  91. data/bin/setup +0 -8
  92. data/data/testdocs/malformed/no_header.adoc +0 -5
  93. data/data/testdocs/toplevel.adoc +0 -19
  94. data/data/testdocs/wellformed/adorned_purpose.adoc +0 -17
  95. data/data/testdocs/wellformed/docidtest/docid_1.adoc +0 -24
  96. data/data/testdocs/wellformed/docidtest/docid_2.adoc +0 -8
  97. data/data/testdocs/wellformed/simple.adoc +0 -14
  98. data/data/testdocs/wellformed/source_highlighting/highlight_source.adoc +0 -38
  99. data/docgen/resources/css/giblish.css +0 -1979
  100. data/docgen/scripts/Jenkinsfile +0 -18
  101. data/docgen/scripts/gen_adoc_org.sh +0 -58
  102. data/docs/README.adoc +0 -387
  103. data/docs/setup_server.adoc +0 -202
  104. data/lib/giblish/buildgraph.rb +0 -216
  105. data/lib/giblish/buildindex.rb +0 -459
  106. data/lib/giblish/core.rb +0 -451
  107. data/lib/giblish/docconverter.rb +0 -308
  108. data/lib/giblish/docid.rb +0 -180
  109. data/lib/giblish/docinfo.rb +0 -75
  110. data/lib/giblish/indexheadings.rb +0 -251
  111. data/lib/giblish-search.cgi +0 -459
  112. data/scripts/hooks/post-receive +0 -57
  113. data/scripts/publish_html.sh +0 -99
@@ -0,0 +1,124 @@
1
+ require "git"
2
+ require "date"
3
+ require_relative "../utils"
4
+ require_relative "../version"
5
+ require_relative "gititf"
6
+ require_relative "gitsummaryprovider"
7
+
8
+ module Giblish
9
+ # acquires a handle to an existing git repo and provide the user
10
+ # with a iteration method 'each_checkout' where each matching branch and/or tag is
11
+ # checked out and presented to the user code.
12
+ class GitCheckoutManager
13
+ attr_reader :branches, :tags, :summary_provider, :git_repo, :repo_root
14
+
15
+ # cmd_opts:: a CmdLine::Options instance
16
+ #
17
+ # Required options:
18
+ # srcdir:: Pathname to the top dir of the local git repo to work with
19
+ # local_only:: if true, do not try to access any remote branches or merge with any
20
+ # upstream changes
21
+ # Optional options:
22
+ # abort_on_error:: bail out at an error or continue. Default 'true'
23
+ # branch_regex:: the regex for the branches to include during iteration (default: none)
24
+ # tag_regex:: the regex for the tags to include during iteration (default: none)
25
+ def initialize(cmd_opts)
26
+ @repo_root = GitItf.find_gitrepo_root(cmd_opts.srcdir)
27
+ raise ArgumentError, "The path: #{cmd_opts.srcdir} is not within a git repo!" if @repo_root.nil?
28
+
29
+ @local_only = cmd_opts.local_only
30
+ @abort_on_error = cmd_opts.abort_on_error.nil? ? true : cmd_opts.abort_on_error
31
+
32
+ @git_repo = init_git_repo(@repo_root, @local_only)
33
+ @branches = select_user_branches(cmd_opts.branch_regex, @local_only)
34
+ @tags = select_user_tags(cmd_opts.tag_regex)
35
+ @summary_provider = GitSummaryDataProvider.new(@repo_root.basename)
36
+ end
37
+
38
+ # present each git checkout matching the init criteria to the user's code.
39
+ #
40
+ # === Example
41
+ #
42
+ # gcm = GitCheckoutManager.new(opts)
43
+ # gcm.each_checkout do |name|
44
+ # ... do things with the currently checked out working tree ...
45
+ # end
46
+ def each_checkout
47
+ current_branch = @git_repo.current_branch
48
+ checkouts = (@branches + @tags)
49
+ if checkouts.empty?
50
+ Giblog.logger.info { "No matching branches or tags found." }
51
+ return
52
+ end
53
+
54
+ checkouts.each do |treeish|
55
+ sync_treeish(treeish)
56
+ # cache branch/tag info for downstream content generation
57
+ @summary_provider.cache_info(@git_repo, treeish)
58
+
59
+ yield(treeish.name)
60
+ rescue => e
61
+ Giblog.logger.error { e.message }
62
+ raise e if @abort_on_error
63
+ end
64
+
65
+ if @git_repo.current_branch != current_branch
66
+ Giblog.logger.info { "Checking out '#{current_branch}'" }
67
+ @git_repo.checkout current_branch
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def sync_treeish(treeish)
74
+ Giblog.logger.info { "Checking out '#{treeish.name}'" }
75
+ @git_repo.checkout treeish.name
76
+
77
+ # merge branches with their upstream at origin unless
78
+ # 'only local'
79
+ unless (treeish.respond_to?(:tag?) && treeish.tag?) || @local_only
80
+ # this is a branch, make sure it is up-to-date
81
+ Giblog.logger.info { "Merging with origin/#{treeish.name}" }
82
+ @git_repo.merge "origin/#{treeish.name}"
83
+ end
84
+ end
85
+
86
+ def init_git_repo(git_repo_root, local_only)
87
+ # Sanity check git repo root
88
+ raise ArgumentError, "No git repo root dir given" unless git_repo_root
89
+
90
+ msg = "Could not find a git repo at #{git_repo_root} !"
91
+ begin
92
+ # Connect to the git repo
93
+ git_repo = Git.open(git_repo_root)
94
+ # fetch all remote refs if ok with user
95
+ msg = "Could not fetch from origin (do you need '--local-only'?)!"
96
+ git_repo.fetch unless local_only
97
+ rescue => e
98
+ raise "#{msg}\n\n#{e.message}"
99
+ end
100
+ git_repo
101
+ end
102
+
103
+ # Get the branches/tags the user wants to parse
104
+ def select_user_branches(regexp, local_only)
105
+ return [] unless regexp
106
+
107
+ user_checkouts = local_only ? @git_repo.branches.local : @git_repo.branches.remote
108
+ user_checkouts.select! do |b|
109
+ # match branches but remove eventual HEAD -> ... entry
110
+ regexp.match b.name unless /^HEAD/.match?(b.name)
111
+ end
112
+ Giblog.logger.debug { "selected git branches: #{user_checkouts.collect { |b| b.name }.join(", ")}" }
113
+ user_checkouts
114
+ end
115
+
116
+ def select_user_tags(regexp)
117
+ return [] unless regexp
118
+
119
+ @git_repo.tags.select do |t|
120
+ regexp.match t.name
121
+ end
122
+ end
123
+ end
124
+ end
@@ -1,5 +1,5 @@
1
1
  require "open3"
2
- require_relative "utils"
2
+ require "git"
3
3
 
4
4
  module Giblish
5
5
  # A home-grown interface class to git. Used for situations when the
@@ -8,14 +8,32 @@ module Giblish
8
8
  attr_reader :repo_root, :git_dir
9
9
 
10
10
  def initialize(path)
11
- @repo_root = Giblish::PathManager.find_gitrepo_root(path)
11
+ @repo_root = GitItf.find_gitrepo_root(path)
12
12
  raise ArgumentError("The path: @{path} is not within a git repo!") if @repo_root.nil?
13
13
 
14
14
  @git_dir = @repo_root / ".git"
15
15
  end
16
16
 
17
+ # Find the root directory of the git repo in which the
18
+ # given dirpath resides.
19
+ #
20
+ # dirpath:: an absolute path to a directory that resides
21
+ # within a git repo.
22
+ #
23
+ # returns:: the root direcotry of the git repo or nil if the input path
24
+ # does not reside within a git repo.
25
+ def self.find_gitrepo_root(dirpath)
26
+ Pathname.new(dirpath).ascend do |p|
27
+ next unless p.exist?
28
+
29
+ git_dir = p.realpath.join(".git")
30
+ return p if git_dir.directory?
31
+ end
32
+ end
33
+
17
34
  # Get the log history of the supplied file as an array of
18
35
  # hashes, each entry has keys:
36
+ #
19
37
  # sha
20
38
  # date
21
39
  # author
@@ -29,6 +47,14 @@ module Giblish
29
47
  process_log_output(o)
30
48
  end
31
49
 
50
+ def current_branch
51
+ # git rev-parse --abbrev-ref HEAD
52
+ o, e, s = exec_cmd("rev-parse", %w[--abbrev-ref HEAD], "")
53
+ raise "Failed to get git log for #{filename}!!\n#{e}" if s.exitstatus != 0
54
+
55
+ o.strip
56
+ end
57
+
32
58
  private
33
59
 
34
60
  # Process the log output from git
@@ -47,7 +73,7 @@ module Giblish
47
73
  end
48
74
 
49
75
  if in_message
50
- hsh["message"] << "#{line[4..-1]}\n"
76
+ hsh["message"] << "#{line[4..]}\n"
51
77
  next
52
78
  end
53
79
 
@@ -58,7 +84,7 @@ module Giblish
58
84
  case key
59
85
  when "commit"
60
86
  hsh_array << hsh if hsh
61
- hsh = { "sha" => value, "message" => "", "parent" => [] }
87
+ hsh = {"sha" => value, "message" => +"", "parent" => []}
62
88
  when "parent"
63
89
  hsh["parent"] << value
64
90
  when "author"
@@ -0,0 +1,61 @@
1
+ = Document repo '<%= @repo_name %>'
2
+ Generated at <%= DateTime.now.strftime("%Y-%m-%d %H:%M:%S") %> by giblish - v<%= Giblish::VERSION %>
3
+
4
+ // Display one table for each branch
5
+ <% unless @branch_info.empty? %>
6
+ Branches::
7
+
8
+ <% @branch_info.each do |b|
9
+ c = b.latest_commit
10
+ %>
11
+
12
+ xref:<%= index_path(b.name) %>[*<%= b.name %>*]
13
+ [cols="1a,5a"]
14
+ |===
15
+
16
+ 2+^|_last committed to <%= c.datetime.strftime("%Y-%m-%d") %> by <%= c.committer %>._
17
+
18
+ |*Sha*
19
+ |<%= c.sha[0..7] %> ...
20
+
21
+ |*Message*
22
+ |<%= c.message %>
23
+
24
+ |===
25
+
26
+
27
+ <% end %>
28
+ <% end %>
29
+
30
+ // Display one table for each tag
31
+ <% unless @tag_infos.empty? %>
32
+ Tags::
33
+
34
+ <% @tag_infos.each do |t|
35
+ c = t.commit
36
+ %>
37
+
38
+ xref:<%= index_path(t.name) %>[*<%= t.name %>*]
39
+ [cols="1a,5a"]
40
+ |===
41
+
42
+ 2+^| _was created <%= t.date.strftime("%Y-%m-%d") %> by <%= t.author %>._
43
+
44
+ |*Message*
45
+ |<%= t.message %>
46
+
47
+ 2+^|_The tag points to the commit made <%= c.datetime.strftime("%Y-%m-%d") %> by <%= c.committer %>._
48
+
49
+ |*Sha*
50
+ |<%= c.sha[0..8] %> ...
51
+
52
+ |*Message*
53
+ |<%= t.commit.message %>
54
+
55
+ |===
56
+
57
+
58
+ <% end %>
59
+ <% end %>
60
+
61
+
@@ -0,0 +1,78 @@
1
+ require "erb"
2
+
3
+ module Giblish
4
+ class GitSummaryDataProvider
5
+ attr_reader :tags, :branches
6
+ attr_accessor :index_basename
7
+
8
+ CommitInfo = Struct.new(:sha, :datetime, :committer, :message)
9
+ TagInfo = Struct.new(:sha, :name, :date, :message, :author, :commit) do
10
+ def id
11
+ Giblish.to_valid_id(name)
12
+ end
13
+ end
14
+ BranchInfo = Struct.new(:name, :latest_commit)
15
+
16
+ DEFAULT_GIT_SUMMARY_TEMPLATE = "/gitsummary.erb"
17
+
18
+ def initialize(repo_name)
19
+ @index_basename = "index"
20
+
21
+ # all these are used by erb
22
+ @repo_name = repo_name
23
+ @branch_info = []
24
+ @tag_infos = []
25
+ end
26
+
27
+ # Cache info on one tag or branch
28
+ #
29
+ # repo:: a handle to a Git repo object
30
+ # treeish:: either a Git::Tag or a Git::Branch object
31
+ def cache_info(repo, treeish)
32
+ if treeish.respond_to?(:tag?) && treeish.tag?
33
+ @tag_infos.push(
34
+ cache_tag_info(repo, treeish)
35
+ ).sort_by!(&:date).reverse!
36
+ else
37
+ @branch_info.push(cache_branch_info(repo, treeish))
38
+ end
39
+ end
40
+
41
+ # returns:: a string with the relative path to the index file
42
+ # in the given branch/tag subtree
43
+ def index_path(treeish_name)
44
+ Giblish.to_fs_str(treeish_name) + "/" + @index_basename + ".adoc"
45
+ end
46
+
47
+ def source
48
+ erb_template = File.read(__dir__ + DEFAULT_GIT_SUMMARY_TEMPLATE)
49
+ ERB.new(erb_template, trim_mode: "<>").result(binding)
50
+ end
51
+
52
+ private
53
+
54
+ def cache_tag_info(repo, tag)
55
+ unless tag.annotated?
56
+ c = repo.gcommit(tag)
57
+ commit = CommitInfo.new(c.sha, c.date, c.author.name, c.message)
58
+ return TagInfo.new(tag.sha, tag.name, c.date, "<No message - this is not an annotated tag>", c.committer.name, commit)
59
+ end
60
+
61
+ # Handle annotated tags
62
+ # get sha of the associated commit. (a bit convoluted...)
63
+ c = repo.gcommit(tag.contents_array[0].split(" ")[1])
64
+ commit = CommitInfo.new(c.sha, c.date, c.author.name, c.message)
65
+ TagInfo.new(tag.sha, tag.name, tag.tagger.date, tag.message, tag.tagger.name, commit)
66
+ end
67
+
68
+ def cache_branch_info(repo, branch)
69
+ # get sha of the associated commit. (a bit convoluted...)
70
+ c = repo.gcommit(branch)
71
+ commit = CommitInfo.new(c.sha, c.date, c.author.name, c.message)
72
+
73
+ # puts c.instance_variables
74
+
75
+ BranchInfo.new(branch.name, commit)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,41 @@
1
+ require_relative "../subtreeinfobuilder"
2
+
3
+ module Giblish
4
+ # Adds a 'FileHistory' instance to each file node's data delegator.
5
+ # Users down-the-line can then call node.data.history to receive
6
+ # an Array of HistoryEntry objects.
7
+ class AddHistoryPostBuilder
8
+ def initialize(repo_root)
9
+ @git_itf = GitItf.new(repo_root)
10
+ end
11
+
12
+ # Called from TreeConverter during post build phase
13
+ def on_postbuild(src_tree, dst_tree, converter)
14
+ current_branch = @git_itf.current_branch
15
+
16
+ dst_tree.traverse_preorder do |level, dst_node|
17
+ unless dst_node.leaf?
18
+ dst_node.data = DataDelegator.new if dst_node.data.nil?
19
+ dst_node.data.add(FileHistory.new(current_branch))
20
+ next
21
+ end
22
+ # next unless dst_node.leaf?
23
+
24
+ src_node = dst_node.data.src_node
25
+ next unless src_node.pathname.exist?
26
+
27
+ # Get the commit history of the doc as an Array of entries
28
+ file_log = FileHistory.new(current_branch)
29
+ @git_itf.file_log(src_node.pathname.to_s).each do |log_entry|
30
+ file_log.history << FileHistory::LogEntry.new(
31
+ log_entry["date"],
32
+ log_entry["author"],
33
+ log_entry["message"],
34
+ log_entry["sha"]
35
+ )
36
+ end
37
+ dst_node.data.add(file_log)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,88 @@
1
+ # Generate asciidoc that represents a given pathtree as a
2
+ # verbatim block with indented, clickable entries.
3
+ class D3TreeGraph
4
+ attr_reader :tree
5
+
6
+ # tree: PathTree
7
+ # === Required node data methods
8
+ # title
9
+ # docid
10
+ #
11
+ # options:
12
+ # dir_index_base_name: String - the basename of the index file
13
+ # residing in each directory
14
+ def initialize(tree:, options: {dir_index_base_name: "index"})
15
+ @tree = transform_data(tree)
16
+ @options = options
17
+ end
18
+
19
+ def source
20
+ erb_template = File.read("#{__dir__}/templates/tree.html.erb")
21
+ ERB.new(erb_template, trim_mode: "<>").result(binding)
22
+ end
23
+
24
+ private
25
+
26
+ # 1 1
27
+ # / | \ |-2
28
+ # 2 5 6 -> | |-3
29
+ # /\ / \ | |-4
30
+ # 3 4 7 8 |-5
31
+ # |-6
32
+ # |-7
33
+ # |-8
34
+ def transform_data(tree)
35
+ data = {}
36
+ last_level = 0
37
+ path = []
38
+ # root -> left -> right
39
+ tree.traverse_preorder do |level, node|
40
+ d = node.leaf? ? leaf_info(node) : directory_info(node)
41
+
42
+ if level == 0
43
+ data = d
44
+ path << d
45
+ elsif level > last_level
46
+ path[-1][:children] << d
47
+ path << d
48
+ elsif level == last_level
49
+ path[-2][:children] << d
50
+ path[-1] = d
51
+ else
52
+ path[level - 1][:children] << d
53
+ path[level] = d
54
+ path = path[0..level]
55
+ end
56
+ last_level = level
57
+ end
58
+ data
59
+ end
60
+
61
+ def leaf_info(node)
62
+ conv_info = node.data
63
+ if conv_info.converted
64
+ name = (conv_info.docid.nil? ? "" : "#{conv_info.docid} - ") + conv_info.title
65
+ dst_ref = conv_info.src_rel_path.sub_ext(".html")
66
+ {
67
+ name: name,
68
+ dst_ref: dst_ref,
69
+ children: []
70
+ }
71
+ else
72
+ Giblog.logger.warn { "Could not get node data for #{conv_info.src_basename}" }
73
+ {
74
+ name: "ERR: Failed Conversion",
75
+ dst_ref: "",
76
+ children: []
77
+ }
78
+ end
79
+ end
80
+
81
+ def directory_info(node)
82
+ {
83
+ name: node.segment,
84
+ dst_ref: "",
85
+ children: []
86
+ }
87
+ end
88
+ end
@@ -0,0 +1,109 @@
1
+ require_relative "dotdigraphadoc"
2
+
3
+ module Giblish
4
+ # Provide the source for a graphviz-based index page.
5
+ #
6
+ # Note: The asciidoctor-diagram API seems a bit strange when it comes to storing
7
+ # temporary files:
8
+ # * it uses a document attribute {imagesoutdir} and stores the
9
+ # generated svg image under that regardless of svg-type.
10
+ # * it uses an option in the diagram macro "cachedir" under which a cached image
11
+ # is stored.
12
+ class GraphPageBase
13
+ attr_reader :adoc_source
14
+
15
+ def initialize(info_2_ids, dst_node, basename, opts = {})
16
+ # use a tmp dir since asciidoctor-diagram generates cache files
17
+ # that it doesn't remove afterwards
18
+ Dir.mktmpdir do |dir|
19
+ # build the graph source
20
+ graph = DotDigraphAdoc.new(
21
+ info_2_ids: info_2_ids,
22
+ opts: {"svg-type" => "inline", "cachedir" => dir}
23
+ )
24
+
25
+ @adoc_source = <<~DEPGRAPH_PAGE
26
+ = Dependency graph
27
+ :imagesoutdir: #{dir}
28
+
29
+ #{graph.source}
30
+
31
+ DEPGRAPH_PAGE
32
+ end
33
+ end
34
+ end
35
+
36
+ # Generates a summary page with a docid-based dependency graph for an entire destination
37
+ # tree.
38
+ class DependencyGraphPostBuilder
39
+ # the dependency graph relies on graphwiz (dot), check if we can access that
40
+ def self.dot_supported
41
+ !Giblish.which("dot").nil?
42
+ end
43
+
44
+ DEFAULT_BASENAME = "gibgraph"
45
+
46
+ # node_2_ids:: a { src_node => [doc id refs]} hash. It will be queried during
47
+ # the post-build phase => it must be populated before the end of the
48
+ # build phase
49
+ def initialize(node_2_ids, docattr_provider = nil, api_opt_provider = nil, adoc_src_provider = nil, basename = DEFAULT_BASENAME)
50
+ # this class relies on graphwiz (dot), make sure we can access it
51
+ raise "Could not find the 'dot' tool needed to generate a dependency graph!" unless DependencyGraphPostBuilder.dot_supported
52
+
53
+ # require asciidoctor module needed for generating diagrams
54
+ require "asciidoctor-diagram/graphviz"
55
+
56
+ @node_2_ids = node_2_ids
57
+ @docattr_provider = docattr_provider
58
+ @api_opt_provider = api_opt_provider
59
+ @adoc_src_provider = adoc_src_provider || GraphPageBase
60
+ @basename = basename
61
+
62
+ @adoc_source = nil
63
+ end
64
+
65
+ def document_attributes(src_node, dst_node, dst_top)
66
+ @docattr_provider.nil? ? {} : @docattr_provider.document_attributes(src_node, dst_node, dst_top)
67
+ end
68
+
69
+ def api_options(src_node, dst_node, dst_top)
70
+ @api_opt_provider.nil? ? {} : @api_opt_provider.api_options(dst_top)
71
+ end
72
+
73
+ def adoc_source(src_node, dst_node, dst_top)
74
+ @adoc_source
75
+ end
76
+
77
+ # Called from TreeConverter during post build phase
78
+ def on_postbuild(src_tree, dst_tree, converter)
79
+ return unless DependencyGraphPostBuilder.dot_supported
80
+
81
+ # convert {src_node => [doc ids]} to {conv_info => [doc ids]}
82
+ info_2_ids = {}
83
+ dst_tree.traverse_preorder do |_level, dst_node|
84
+ next unless dst_node.leaf?
85
+
86
+ sn = dst_node.data.src_node
87
+ info_2_ids[dst_node] = @node_2_ids[sn] if @node_2_ids.key?(sn)
88
+ end
89
+
90
+ # add a virtual 'gibgraph.adoc' node as the only node in a source tree
91
+ # with this object as provider of both adoc source and conversion options
92
+ v_srcpath = Pathname.new("/virtual") / "#{@basename}.adoc"
93
+ src_node = PathTree.new(v_srcpath, self).node(v_srcpath, from_root: true)
94
+
95
+ # add the destination node where the converted file will be stored
96
+ i_node = dst_tree.add_descendants(@basename)
97
+
98
+ # get the adoc source from the provider (Class or instance)
99
+ @adoc_source = if @adoc_src_provider.is_a?(Class)
100
+ @adoc_src_provider.new(info_2_ids, dst_tree, @basename).adoc_source
101
+ else
102
+ @adoc_src_provider.adoc_source
103
+ end
104
+
105
+ # do the conversion
106
+ converter.convert(src_node, i_node, dst_tree)
107
+ end
108
+ end
109
+ end