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.
- checksums.yaml +4 -4
- data/.github/workflows/unit_tests.yml +30 -0
- data/.gitignore +7 -3
- data/.ruby-version +1 -1
- data/Changelog.adoc +59 -0
- data/README.adoc +261 -0
- data/docs/concepts/text_search.adoc +213 -0
- data/docs/concepts/text_search_im/cgi-search_request.puml +35 -0
- data/docs/concepts/text_search_im/cgi-search_request.svg +397 -0
- data/docs/concepts/text_search_im/search_request.puml +40 -0
- data/docs/concepts/text_search_im/search_request.svg +408 -0
- data/docs/howtos/trigger_generation.adoc +180 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/Render Documents.png +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/View Documents.png +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.graphml +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.svg +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.graphml +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.svg +0 -0
- data/docs/howtos/trigger_generation_im/docgen_github.puml +51 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/giblish_deployment.graphml +0 -0
- data/docs/howtos/trigger_generation_im/post-receive-example.sh +50 -0
- data/docs/reference/box_flow_spec.adoc +22 -0
- data/docs/reference/search_spec.adoc +185 -0
- data/giblish.gemspec +54 -32
- data/lib/giblish/adocsrc_providers.rb +23 -0
- data/lib/giblish/application.rb +214 -41
- data/lib/giblish/cmdline.rb +273 -259
- data/lib/giblish/config_utils.rb +41 -0
- data/lib/giblish/configurator.rb +163 -0
- data/lib/giblish/conversion_info.rb +120 -0
- data/lib/giblish/docattr_providers.rb +125 -0
- data/lib/giblish/docid/docid.rb +181 -0
- data/lib/giblish/github_trigger/webhook_manager.rb +64 -0
- data/lib/giblish/gitrepos/checkoutmanager.rb +124 -0
- data/lib/giblish/{gititf.rb → gitrepos/gititf.rb} +30 -4
- data/lib/giblish/gitrepos/gitsummary.erb +61 -0
- data/lib/giblish/gitrepos/gitsummaryprovider.rb +78 -0
- data/lib/giblish/gitrepos/history_pb.rb +41 -0
- data/lib/giblish/indexbuilders/d3treegraph.rb +88 -0
- data/lib/giblish/indexbuilders/depgraphbuilder.rb +109 -0
- data/lib/giblish/indexbuilders/dotdigraphadoc.rb +174 -0
- data/lib/giblish/indexbuilders/standard_index.erb +10 -0
- data/lib/giblish/indexbuilders/subtree_indices.rb +132 -0
- data/lib/giblish/indexbuilders/templates/circles.html.erb +111 -0
- data/lib/giblish/indexbuilders/templates/flame.html.erb +61 -0
- data/lib/giblish/indexbuilders/templates/tree.html.erb +366 -0
- data/lib/giblish/indexbuilders/templates/treemap.html.erb +127 -0
- data/lib/giblish/indexbuilders/verbatimtree.rb +94 -0
- data/lib/giblish/pathtree.rb +473 -74
- data/lib/giblish/resourcepaths.rb +150 -0
- data/lib/giblish/search/expand_adoc.rb +55 -0
- data/lib/giblish/search/headingindexer.rb +312 -0
- data/lib/giblish/search/request_manager.rb +110 -0
- data/lib/giblish/search/searchquery.rb +68 -0
- data/lib/giblish/search/textsearcher.rb +349 -0
- data/lib/giblish/subtreeinfobuilder.rb +77 -0
- data/lib/giblish/treeconverter.rb +272 -0
- data/lib/giblish/utils.rb +142 -294
- data/lib/giblish/version.rb +1 -1
- data/lib/giblish.rb +10 -7
- data/scripts/hooks/post-receive.example +66 -0
- data/{docgen/scripts/githook_examples → scripts/hooks}/post-update.example +0 -0
- data/{docgen → scripts}/resources/css/adoc-colony.css +0 -0
- data/scripts/resources/css/giblish-serif.css +419 -0
- data/scripts/resources/css/giblish.css +1979 -419
- data/{docgen → scripts}/resources/fonts/Ubuntu-B.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-BI.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-R.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-RI.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/mplus1p-regular-fallback.ttf +0 -0
- data/{docgen → scripts}/resources/images/giblish_logo.png +0 -0
- data/{docgen → scripts}/resources/images/giblish_logo.svg +0 -0
- data/{docgen → scripts}/resources/themes/giblish.yml +0 -0
- data/scripts/wserv_development.rb +32 -0
- data/web_apps/cgi_search/gibsearch.rb +43 -0
- data/web_apps/gh_webhook_trigger/config.ru +2 -0
- data/web_apps/gh_webhook_trigger/gh_webhook_trigger.rb +73 -0
- data/web_apps/gh_webhook_trigger/public/dummy.txt +3 -0
- data/web_apps/sinatra_search/config.ru +2 -0
- data/web_apps/sinatra_search/public/dummy.txt +3 -0
- data/web_apps/sinatra_search/sinatra_search.rb +34 -0
- data/web_apps/sinatra_search/tmp/restart.txt +0 -0
- metadata +188 -85
- data/.rubocop.yml +0 -7
- data/.travis.yml +0 -3
- data/Changelog +0 -16
- data/Gemfile +0 -4
- data/README.adoc +0 -1
- data/Rakefile +0 -41
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/data/testdocs/malformed/no_header.adoc +0 -5
- data/data/testdocs/toplevel.adoc +0 -19
- data/data/testdocs/wellformed/adorned_purpose.adoc +0 -17
- data/data/testdocs/wellformed/docidtest/docid_1.adoc +0 -24
- data/data/testdocs/wellformed/docidtest/docid_2.adoc +0 -8
- data/data/testdocs/wellformed/simple.adoc +0 -14
- data/data/testdocs/wellformed/source_highlighting/highlight_source.adoc +0 -38
- data/docgen/resources/css/giblish.css +0 -1979
- data/docgen/scripts/Jenkinsfile +0 -18
- data/docgen/scripts/gen_adoc_org.sh +0 -58
- data/docs/README.adoc +0 -387
- data/docs/setup_server.adoc +0 -202
- data/lib/giblish/buildgraph.rb +0 -216
- data/lib/giblish/buildindex.rb +0 -459
- data/lib/giblish/core.rb +0 -451
- data/lib/giblish/docconverter.rb +0 -308
- data/lib/giblish/docid.rb +0 -180
- data/lib/giblish/docinfo.rb +0 -75
- data/lib/giblish/indexheadings.rb +0 -251
- data/lib/giblish-search.cgi +0 -459
- data/scripts/hooks/post-receive +0 -57
- data/scripts/publish_html.sh +0 -99
@@ -0,0 +1,163 @@
|
|
1
|
+
require_relative "config_utils"
|
2
|
+
require_relative "resourcepaths"
|
3
|
+
require_relative "docattr_providers"
|
4
|
+
require_relative "adocsrc_providers"
|
5
|
+
require_relative "subtreeinfobuilder"
|
6
|
+
require_relative "docid/docid"
|
7
|
+
require_relative "search/headingindexer"
|
8
|
+
require_relative "indexbuilders/depgraphbuilder"
|
9
|
+
require_relative "indexbuilders/subtree_indices"
|
10
|
+
require_relative "gitrepos/history_pb"
|
11
|
+
|
12
|
+
module Giblish
|
13
|
+
class HtmlLayoutConfig
|
14
|
+
attr_reader :pre_builders, :post_builders, :adoc_extensions, :adoc_api_opts, :docattr_providers
|
15
|
+
|
16
|
+
def initialize(config_opts)
|
17
|
+
@adoc_api_opts = {backend: "html"}
|
18
|
+
@pre_builders = []
|
19
|
+
@post_builders = []
|
20
|
+
@adoc_extensions = {}
|
21
|
+
@docattr_providers = []
|
22
|
+
case config_opts
|
23
|
+
in resource_dir:
|
24
|
+
# copy local resources to dst and link the generated html with
|
25
|
+
# the given css
|
26
|
+
@pre_builders << CopyResourcesPreBuild.new(config_opts)
|
27
|
+
|
28
|
+
# make sure generated html has relative link to the copied css
|
29
|
+
@docattr_providers << RelativeCssDocAttr.new(ResourcePaths.new(config_opts).dst_style_path_rel)
|
30
|
+
in server_css:
|
31
|
+
# do not copy any local resources, use the given web path to link to css
|
32
|
+
@docattr_providers << AbsoluteLinkedCss.new(config_opts.server_css)
|
33
|
+
else
|
34
|
+
4 == 5 # workaround for bug in standardrb formatting
|
35
|
+
end
|
36
|
+
|
37
|
+
if config_opts.make_searchable
|
38
|
+
# enabling text search
|
39
|
+
search_provider = HeadingIndexer.new(config_opts.srcdir)
|
40
|
+
@adoc_extensions[:tree_processor] = search_provider
|
41
|
+
@post_builders << search_provider
|
42
|
+
|
43
|
+
# add search form to all docs
|
44
|
+
@adoc_extensions[:docinfo_processor] = AddSearchForm.new(config_opts.search_action_path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class PdfLayoutConfig
|
50
|
+
attr_reader :pre_builders, :post_builders, :adoc_extensions, :adoc_api_opts, :docattr_providers
|
51
|
+
|
52
|
+
def initialize(config_opts)
|
53
|
+
@adoc_api_opts = {backend: "pdf"}
|
54
|
+
@pre_builders = []
|
55
|
+
@post_builders = []
|
56
|
+
@adoc_extensions = {}
|
57
|
+
@docattr_providers = []
|
58
|
+
|
59
|
+
unless config_opts.resource_dir.nil?
|
60
|
+
# generate pdf using asciidoctor-pdf with custom styling
|
61
|
+
rp = ResourcePaths.new(config_opts)
|
62
|
+
@docattr_providers << PdfCustomStyle.new(rp.src_style_path_abs, *rp.font_dirs_abs.to_a)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# configure all parts needed to execute the options specified by
|
68
|
+
# the user
|
69
|
+
class Configurator
|
70
|
+
attr_reader :build_options, :doc_attr, :config_opts
|
71
|
+
|
72
|
+
# config_opts:: a Cmdline::Options instance with config info
|
73
|
+
def initialize(config_opts)
|
74
|
+
@config_opts = config_opts
|
75
|
+
@build_options = {
|
76
|
+
pre_builders: [],
|
77
|
+
post_builders: [],
|
78
|
+
adoc_api_opts: {},
|
79
|
+
# add a hash where all values are initiated as empty arrays
|
80
|
+
adoc_extensions: Hash.new { |h, k| h[k] = [] }
|
81
|
+
}
|
82
|
+
|
83
|
+
# Initiate the doc attribute repo used during 'run-time'
|
84
|
+
@doc_attr = DocAttrBuilder.new(
|
85
|
+
GiblishDefaultDocAttribs.new
|
86
|
+
)
|
87
|
+
|
88
|
+
layout_config = case config_opts
|
89
|
+
in format: "html" then HtmlLayoutConfig.new(config_opts)
|
90
|
+
in format: "pdf" then PdfLayoutConfig.new(config_opts)
|
91
|
+
else
|
92
|
+
raise OptionParser::InvalidArgument, "The given cmd line flags are not supported: #{config_opts.inspect}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# setup all options from the chosen layout configuration but
|
96
|
+
# override doc attributes with ones from the supplied configuration to
|
97
|
+
# ensure they have highest pref
|
98
|
+
@doc_attr.add_doc_attr_providers(
|
99
|
+
*layout_config.docattr_providers, CmdLineDocAttribs.new(config_opts)
|
100
|
+
)
|
101
|
+
|
102
|
+
setup_docid(config_opts, @build_options, @doc_attr)
|
103
|
+
setup_index_generation(config_opts, @build_options, @doc_attr)
|
104
|
+
|
105
|
+
# setup all pre,post, and build options
|
106
|
+
@build_options[:adoc_api_opts] = layout_config.adoc_api_opts
|
107
|
+
@build_options[:pre_builders] += layout_config.pre_builders
|
108
|
+
@build_options[:post_builders] += layout_config.post_builders
|
109
|
+
layout_config.adoc_extensions.each do |type, instance|
|
110
|
+
@build_options[:adoc_extensions][type] << instance
|
111
|
+
end
|
112
|
+
|
113
|
+
# add copy of asset dirs if options stipulates this
|
114
|
+
@build_options[:post_builders] << CopyAssetDirsPostBuild.new(@config_opts) unless @config_opts.copy_asset_folders.nil?
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
|
119
|
+
def setup_index_generation(config_opts, build_options, doc_attr)
|
120
|
+
return if config_opts.no_index
|
121
|
+
|
122
|
+
# setup index generation
|
123
|
+
idx = SubtreeInfoBuilder.new(doc_attr, nil, SubtreeIndexBase, config_opts.index_basename)
|
124
|
+
build_options[:post_builders] << idx
|
125
|
+
end
|
126
|
+
|
127
|
+
def setup_docid(config_opts, build_options, doc_attr)
|
128
|
+
return unless config_opts.resolve_docid
|
129
|
+
|
130
|
+
# setup docid resolution
|
131
|
+
d = DocIdExtension::DocidPreBuilder.new
|
132
|
+
build_options[:pre_builders] << d
|
133
|
+
docid_pp = DocIdExtension::DocidProcessor.new({id_2_node: d.id_2_node})
|
134
|
+
build_options[:adoc_extensions][:preprocessor] << docid_pp
|
135
|
+
|
136
|
+
# early exit if user does not want indices
|
137
|
+
return if config_opts.no_index
|
138
|
+
|
139
|
+
# generate dep graph if graphviz is available
|
140
|
+
dg = DependencyGraphPostBuilder.new(docid_pp.node_2_ids, doc_attr, nil, nil, config_opts.graph_basename)
|
141
|
+
build_options[:post_builders] << dg
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# swap standard index generation from the base class to ones including
|
146
|
+
# git history.
|
147
|
+
class GitRepoConfigurator < Configurator
|
148
|
+
def initialize(config_opts, git_repo_dir)
|
149
|
+
@git_repo_dir = git_repo_dir
|
150
|
+
config_opts.search_action_path ||= "/gibsearch.cgi"
|
151
|
+
super(config_opts)
|
152
|
+
end
|
153
|
+
|
154
|
+
protected
|
155
|
+
|
156
|
+
def setup_index_generation(config_opts, build_options, doc_attr)
|
157
|
+
return if config_opts.no_index
|
158
|
+
|
159
|
+
build_options[:post_builders] << AddHistoryPostBuilder.new(@git_repo_dir)
|
160
|
+
build_options[:post_builders] << SubtreeInfoBuilder.new(doc_attr, nil, SubtreeIndexGit, config_opts.index_basename)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Giblish
|
4
|
+
class FileHistory
|
5
|
+
# History info from git
|
6
|
+
LogEntry = Struct.new(:date, :author, :message, :sha1)
|
7
|
+
|
8
|
+
attr_accessor :history, :branch
|
9
|
+
|
10
|
+
def initialize(branch)
|
11
|
+
@branch = branch
|
12
|
+
@history = []
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Base class for bundling together data we always cache for
|
17
|
+
# each asciidoc file we come across.
|
18
|
+
#
|
19
|
+
# Users are expected to use the derived classes.
|
20
|
+
class ConversionInfo
|
21
|
+
attr_reader :converted, :src_node, :dst_node, :dst_top
|
22
|
+
|
23
|
+
# The relative Pathname from the root dir to the src doc
|
24
|
+
# Ex Pathname("my/subdir/file1.adoc")
|
25
|
+
attr_reader :src_rel_path
|
26
|
+
|
27
|
+
@@nof_missing_titles = 0
|
28
|
+
|
29
|
+
def initialize(converted:, src_node:, dst_node:, dst_top:)
|
30
|
+
@converted = converted
|
31
|
+
@src_node = src_node
|
32
|
+
@dst_node = dst_node
|
33
|
+
@dst_top = dst_top
|
34
|
+
@title = nil
|
35
|
+
|
36
|
+
@src_rel_path = dst_node.relative_path_from(dst_top).dirname / src_node.pathname.basename
|
37
|
+
end
|
38
|
+
|
39
|
+
# return:: a String with the basename of the source file
|
40
|
+
def src_basename
|
41
|
+
@src_node.pathname.basename.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def title
|
45
|
+
return @title if @title
|
46
|
+
|
47
|
+
"NO TITLE FOUND (#{@@nof_missing_titles += 1}) !"
|
48
|
+
end
|
49
|
+
|
50
|
+
def docid
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
"Conversion #{@converted ? "succeeded" : "failed"} - src: #{@src_node.pathname} dst: #{@dst_node.pathname}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Provide data and access methods available when a conversion has
|
60
|
+
# succeeded
|
61
|
+
class SuccessfulConversion < ConversionInfo
|
62
|
+
attr_reader :stderr, :adoc
|
63
|
+
|
64
|
+
# The relative Pathname from the root dir to the dst file
|
65
|
+
# Ex Pathname("my/subdir/file1.html")
|
66
|
+
attr_reader :dst_rel_path
|
67
|
+
|
68
|
+
attr_reader :purpose_str
|
69
|
+
|
70
|
+
def initialize(src_node:, dst_node:, dst_top:, adoc:, adoc_stderr: "")
|
71
|
+
super(converted: true, src_node: src_node, dst_node: dst_node, dst_top: dst_top)
|
72
|
+
|
73
|
+
@adoc = adoc
|
74
|
+
@stderr = adoc_stderr
|
75
|
+
@title = @adoc.doctitle
|
76
|
+
|
77
|
+
@dst_rel_path = dst_node.relative_path_from(dst_top)
|
78
|
+
|
79
|
+
# Cach the purpose info if it exists
|
80
|
+
@purpose_str = get_purpose_info adoc
|
81
|
+
end
|
82
|
+
|
83
|
+
def docid
|
84
|
+
@adoc.attributes["docid"]
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# TODO: Move this somewhere else
|
90
|
+
def get_purpose_info(adoc)
|
91
|
+
# Get the 'Purpose' section if it exists
|
92
|
+
purpose_str = +""
|
93
|
+
adoc.blocks.each do |section|
|
94
|
+
next unless section.is_a?(Asciidoctor::Section) &&
|
95
|
+
(section.level == 1) &&
|
96
|
+
(section.name =~ /^Purpose$/)
|
97
|
+
|
98
|
+
# filter out 'odd' text, such as lists etc...
|
99
|
+
section.blocks.each do |bb|
|
100
|
+
next unless bb.is_a?(Asciidoctor::Block)
|
101
|
+
|
102
|
+
purpose_str << "#{bb.source}\n+\n"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
purpose_str
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Provide data and access methods available when a conversion has
|
110
|
+
# failed
|
111
|
+
class FailedConversion < ConversionInfo
|
112
|
+
attr_reader :error_msg
|
113
|
+
|
114
|
+
def initialize(src_node:, dst_node:, dst_top:, error_msg: nil)
|
115
|
+
super(converted: false, src_node: src_node, dst_node: dst_node, dst_top: dst_top)
|
116
|
+
|
117
|
+
@error_msg = error_msg
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Giblish
|
2
|
+
class DocAttributesBase
|
3
|
+
def document_attributes(src_node, dst_node, dst)
|
4
|
+
raise NotImplementedError
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class AbsoluteLinkedCss < DocAttributesBase
|
9
|
+
def initialize(css_path)
|
10
|
+
@css_path = Pathname.new(css_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def document_attributes(src_node, dst_node, dst_top)
|
14
|
+
{
|
15
|
+
"stylesdir" => @css_path.dirname.to_s,
|
16
|
+
"stylesheet" => @css_path.basename.to_s,
|
17
|
+
"linkcss" => true,
|
18
|
+
"copycss" => nil
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class RelativeCssDocAttr < DocAttributesBase
|
24
|
+
# dst_css_path_rel:: the relative path to the dst top to the location of the
|
25
|
+
# css file to use
|
26
|
+
def initialize(dst_css_path_rel)
|
27
|
+
@css_path = Pathname.new(dst_css_path_rel)
|
28
|
+
end
|
29
|
+
|
30
|
+
def document_attributes(src_node, dst_node, dst_top)
|
31
|
+
href_path = dst_top.relative_path_from(dst_node).dirname / @css_path
|
32
|
+
{
|
33
|
+
"stylesdir" => href_path.dirname.to_s,
|
34
|
+
"stylesheet" => href_path.basename.to_s,
|
35
|
+
"linkcss" => true,
|
36
|
+
"copycss" => nil
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class PdfCustomStyle < DocAttributesBase
|
42
|
+
# pdf_style_path:: the path name (preferable absolute) to the yml file
|
43
|
+
# pdf_font_dirs:: one or more Pathnames to each font dir that shall be
|
44
|
+
# checked for fonts
|
45
|
+
def initialize(pdf_style_path, *pdf_font_dirs)
|
46
|
+
@pdf_theme_path = pdf_style_path
|
47
|
+
# one can specify multiple font dirs as:
|
48
|
+
# -a pdf-fontsdir="path/to/fonts;path/to/more-fonts"
|
49
|
+
# Always use the GEM_FONTS_DIR token to load the adoc-pdf gem's font dirs as well
|
50
|
+
@pdf_fontsdir = (Array(pdf_font_dirs) << "GEM_FONTS_DIR").collect { |d| d.to_s }&.join(";")
|
51
|
+
end
|
52
|
+
|
53
|
+
def document_attributes(src_node, dst_node, dst_top)
|
54
|
+
result = {
|
55
|
+
"pdf-theme" => @pdf_theme_path.basename.to_s,
|
56
|
+
"pdf-themesdir" => @pdf_theme_path.dirname.to_s,
|
57
|
+
"icons" => "font"
|
58
|
+
}
|
59
|
+
result["pdf-fontsdir"] = @pdf_fontsdir unless @pdf_fontsdir.nil?
|
60
|
+
result
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# provides the default, opinionated doc attributes used
|
65
|
+
# when nothing else is set
|
66
|
+
class GiblishDefaultDocAttribs
|
67
|
+
def document_attributes(src_node, dst_node, dst_top)
|
68
|
+
{
|
69
|
+
"source-highlighter" => "rouge",
|
70
|
+
"rouge-css" => "style",
|
71
|
+
"rouge-style" => "github",
|
72
|
+
"xrefstyle" => "short",
|
73
|
+
"icons" => "font"
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class CmdLineDocAttribs
|
79
|
+
def initialize(cmd_opts)
|
80
|
+
@cmdline_attrs = cmd_opts.doc_attributes.dup
|
81
|
+
end
|
82
|
+
|
83
|
+
def document_attributes(src_node, dst_node, dst_top)
|
84
|
+
@cmdline_attrs
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# a builder class that can be setup with one or more document
|
89
|
+
# attribute providers and then used as the sole doc attrib provider
|
90
|
+
# where the merged sum of all added providers will be presented.
|
91
|
+
#
|
92
|
+
# If more than one added provider set the same doc attrib, the last
|
93
|
+
# added has preference.
|
94
|
+
#
|
95
|
+
# Each added provider must conform to the itf defined in
|
96
|
+
# the DocAttributesBase class.
|
97
|
+
class DocAttrBuilder
|
98
|
+
attr_reader :providers
|
99
|
+
|
100
|
+
def initialize(*attr_providers)
|
101
|
+
@providers = []
|
102
|
+
add_doc_attr_providers(*attr_providers)
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_doc_attr_providers(*attr_providers)
|
106
|
+
return if attr_providers.empty?
|
107
|
+
|
108
|
+
# check itf compliance of added providers
|
109
|
+
attr_providers.each do |ap|
|
110
|
+
unless ap.respond_to?(:document_attributes) &&
|
111
|
+
ap.method(:document_attributes).arity == 3
|
112
|
+
raise ArgumentError, "The supplied doc attribute provider of type: #{ap.class} did not conform to the interface"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
@providers += attr_providers
|
117
|
+
end
|
118
|
+
|
119
|
+
def document_attributes(src_node, dst_node, dst_top)
|
120
|
+
result = {}
|
121
|
+
@providers.each { |p| result.merge!(p.document_attributes(src_node, dst_node, dst_top)) }
|
122
|
+
result
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require "asciidoctor"
|
2
|
+
require_relative "../pathtree"
|
3
|
+
|
4
|
+
module Giblish
|
5
|
+
module DocIdExtension
|
6
|
+
# Build a hash of {docid => src_node} that can be used to resolve
|
7
|
+
# doc id references to valid dst paths
|
8
|
+
class DocidPreBuilder
|
9
|
+
attr_accessor :id_2_node
|
10
|
+
|
11
|
+
# The minimum number of characters required for a valid doc id
|
12
|
+
ID_MIN_LENGTH = 2
|
13
|
+
|
14
|
+
# The maximum number of characters required for a valid doc id
|
15
|
+
ID_MAX_LENGTH = 10
|
16
|
+
|
17
|
+
# the regex used to find :docid: entries in the doc header
|
18
|
+
DOCID_REGEX = /^:docid: +(.*)$/
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@id_2_node = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# called during the pre-build phase
|
25
|
+
def on_prebuild(src_tree, dst_tree, converter)
|
26
|
+
src_tree.traverse_preorder do |level, src_node|
|
27
|
+
next unless src_node.leaf?
|
28
|
+
|
29
|
+
parse_node(src_node)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Check if a :docid: <id> entry exists in the header of the doc
|
36
|
+
# denoted by 'node'.
|
37
|
+
# According to http://www.methods.co.nz/asciidoc/userguide.html#X95
|
38
|
+
# the header is optional, but if it exists it:
|
39
|
+
# - must start with a titel (=+ <My Title>)
|
40
|
+
# - ends with one or more blank lines
|
41
|
+
# - does not contain any blank line
|
42
|
+
def parse_node(node)
|
43
|
+
p = node.pathname
|
44
|
+
Giblog.logger.debug { "parsing file #{p} for docid..." }
|
45
|
+
Giblish.process_header_lines_from_file(p) do |line|
|
46
|
+
next unless DOCID_REGEX.match(line)
|
47
|
+
|
48
|
+
# There is a docid defined, cache the path and doc id
|
49
|
+
id = Regexp.last_match(1).strip
|
50
|
+
Giblog.logger.debug { "found possible docid: #{id}" }
|
51
|
+
if doc_id_ok?(id, p)
|
52
|
+
@id_2_node[id] = node
|
53
|
+
else
|
54
|
+
Giblog.logger.error { "Invalid docid: #{id} in file #{p}, this will be ignored!" }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# make sure the id is within the designated length and
|
60
|
+
# does not contain a '#' symbol
|
61
|
+
def doc_id_ok?(doc_id, path)
|
62
|
+
if @id_2_node.key? doc_id
|
63
|
+
Giblog.logger.warn { "Found same doc id twice: (#{doc_id}). This id will be associated with '#{path}' and _not_ with '#{@id_2_node[id]}'." }
|
64
|
+
end
|
65
|
+
(doc_id.length.between?(ID_MIN_LENGTH, ID_MAX_LENGTH) && !doc_id.include?("#"))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# A preprocessor extension to the Asciidoctor engine that transforms all
|
70
|
+
# <<:docid:>> references found in the adoc source into the matching
|
71
|
+
# file reference.
|
72
|
+
#
|
73
|
+
# It requiers a populated 'id_2_node' with {docid => src_node} before
|
74
|
+
# the first invokation of the 'process' method via Asciidoctor.
|
75
|
+
#
|
76
|
+
# When running, it builds a publicly available 'node_2_ids' map.
|
77
|
+
class DocidProcessor < Asciidoctor::Extensions::Preprocessor
|
78
|
+
# {src_node => [referenced doc_id's]}
|
79
|
+
attr_reader :node_2_ids
|
80
|
+
|
81
|
+
# required options:
|
82
|
+
#
|
83
|
+
# id_2_node:: {docid => src_node }
|
84
|
+
def initialize(opts)
|
85
|
+
raise ArgumentError, "Missing required option: :id_2_node!" unless opts.key?(:id_2_node)
|
86
|
+
|
87
|
+
super(opts)
|
88
|
+
@id_2_node = opts[:id_2_node]
|
89
|
+
|
90
|
+
# init new keys in the hash with an empty array
|
91
|
+
@node_2_ids = Hash.new { |h, k| h[k] = [] }
|
92
|
+
end
|
93
|
+
|
94
|
+
# The regex that matches docid references in files
|
95
|
+
DOCID_REF_REGEX = /<<\s*:docid:\s*(.*?)>>/
|
96
|
+
PASS_MACRO_REGEX = /pass:\[.*\]/
|
97
|
+
|
98
|
+
# This hook is called by Asciidoctor once for each document _before_
|
99
|
+
# Asciidoctor processes the adoc content.
|
100
|
+
#
|
101
|
+
# It replaces references of the format <<:docid: ID-1234,Hello >> with
|
102
|
+
# references to a resolved relative path.
|
103
|
+
def process(document, reader)
|
104
|
+
# Add doc as a source dependency for doc ids
|
105
|
+
gib_data = document.attributes["giblish-info"]
|
106
|
+
if gib_data.nil?
|
107
|
+
Giblog.logger.error "The docid preprocessor did not find required info in the doc attribute. (Doc title: #{document.title}"
|
108
|
+
return reader
|
109
|
+
end
|
110
|
+
|
111
|
+
src_node = gib_data[:src_node]
|
112
|
+
|
113
|
+
# Convert all docid refs to valid relative refs
|
114
|
+
reader.lines.each do |line|
|
115
|
+
# remove commented lines
|
116
|
+
next if line.start_with?("//")
|
117
|
+
|
118
|
+
@node_2_ids[src_node] += parse_line(line, src_node)
|
119
|
+
end
|
120
|
+
|
121
|
+
# we only care for one ref to a specific target, remove duplicates
|
122
|
+
@node_2_ids[src_node] = @node_2_ids[src_node].uniq
|
123
|
+
|
124
|
+
# the asciidoctor engine wants the reader back
|
125
|
+
reader
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# substitutes docid references with the corresponding relative path
|
131
|
+
# references.
|
132
|
+
#
|
133
|
+
# returns:: Array of found docid references
|
134
|
+
def parse_line(line, src_node)
|
135
|
+
refs = []
|
136
|
+
# remove all content within a 'pass:[]' macro from the parser
|
137
|
+
line.gsub!(PASS_MACRO_REGEX)
|
138
|
+
|
139
|
+
line.gsub!(DOCID_REF_REGEX) do |_m|
|
140
|
+
# parse the ref
|
141
|
+
target_id, section, display_str = parse_doc_id_ref(Regexp.last_match(1))
|
142
|
+
Giblog.logger.debug { "Found docid ref to #{target_id} in file: #{src_node.pathname}..." }
|
143
|
+
|
144
|
+
# make sure it exists in the cache
|
145
|
+
unless @id_2_node.key?(target_id)
|
146
|
+
Giblog.logger.warn { "Could not resolve ref to #{target_id} from file: #{src_node.pathname}..." }
|
147
|
+
break "<<UNKNOWN_DOC, Could not resolve doc id reference !!!>>"
|
148
|
+
end
|
149
|
+
|
150
|
+
# add the referenced doc id as a target dependency of this document
|
151
|
+
refs << target_id
|
152
|
+
|
153
|
+
# get the relative path from this file to the target file
|
154
|
+
target_node = @id_2_node[target_id]
|
155
|
+
rel_path = target_node.pathname.relative_path_from(src_node.pathname.dirname)
|
156
|
+
|
157
|
+
# return the resolved reference
|
158
|
+
"<<#{rel_path}##{section}#{display_str}>>"
|
159
|
+
end
|
160
|
+
refs
|
161
|
+
end
|
162
|
+
|
163
|
+
# input_str shall be the expression between
|
164
|
+
# <<:docid:<input_str>>> where the <input_str> is in the form
|
165
|
+
# <id>[#section][,display_str]
|
166
|
+
#
|
167
|
+
# returns an array with [id, section, display_str]
|
168
|
+
def parse_doc_id_ref(input_str)
|
169
|
+
ref, display_str = input_str.split(",").each(&:strip)
|
170
|
+
id, section = ref.split "#"
|
171
|
+
|
172
|
+
display_str = id.dup if display_str.nil?
|
173
|
+
display_str.prepend ","
|
174
|
+
|
175
|
+
section = "" if section.nil?
|
176
|
+
|
177
|
+
[id, section, display_str]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "git"
|
2
|
+
require_relative "../application"
|
3
|
+
|
4
|
+
module Giblish
|
5
|
+
# Generate documentation using giblish based on the supplied parameters
|
6
|
+
class GenerateFromRefs
|
7
|
+
STANDARD_GIBLISH_FLAGS = %W[-f html -m --copy-asset-folders _assets$ --server-search-path /cgi-bin/gibsearch.cgi]
|
8
|
+
|
9
|
+
# doc_repo_url:: the url of the repo hosting the docs to be generated. this repo will be cloned
|
10
|
+
# ref_regexp:: a regexp that both defines what git refs that trigger a document generation and what refs will be
|
11
|
+
# generated.
|
12
|
+
# clone_dir_parent:: path to the local directory under which the doc_repo_url will be cloned.
|
13
|
+
# clone_name:: the name of the local clone of the doc_repo_url
|
14
|
+
# doc_src_rel:: the relative path from the repo root to the directory where the docs reside
|
15
|
+
# doc_dst_abs:: the absolute path to the target location for the generated docs
|
16
|
+
# logger:: a ruby Logger instance that will receive log messages
|
17
|
+
def initialize(doc_repo_url, ref_regexp, clone_dir_parent, clone_name, giblish_args, doc_src_rel, doc_dst_abs, logger)
|
18
|
+
@doc_repo_url = doc_repo_url
|
19
|
+
@ref_regexp = ref_regexp
|
20
|
+
@giblish_args = STANDARD_GIBLISH_FLAGS
|
21
|
+
@giblish_args += giblish_args unless giblish_args.nil?
|
22
|
+
@doc_src_rel = doc_src_rel
|
23
|
+
@dstdir = doc_dst_abs
|
24
|
+
@logger = logger
|
25
|
+
|
26
|
+
@repo_root = clone(doc_repo_url, clone_dir_parent, clone_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generate documents from the git refs found in the GitHub
|
30
|
+
# webhook payload.
|
31
|
+
def docs_from_gh_webhook(github_hash)
|
32
|
+
# TODO Implement support for other refs than branches
|
33
|
+
ref = github_hash.fetch(:ref).sub("refs/heads/", "")
|
34
|
+
if ref.empty? || !(@ref_regexp =~ ref)
|
35
|
+
@logger&.info { "Ref '#{ref}' does not match the document generation trigger -> No document generation triggereed" }
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
generate_docs(ref)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def clone(doc_repo_url, dst_dir, clone_name)
|
45
|
+
p = Pathname.new(dst_dir).join(clone_name)
|
46
|
+
|
47
|
+
@logger&.info { "Cloning #{doc_repo_url} to #{p}..." }
|
48
|
+
repo = Git.clone(doc_repo_url, clone_name, path: dst_dir.to_s, logger: @logger)
|
49
|
+
repo.config("user.name", "Giblish Webhook Manager")
|
50
|
+
repo.config("user.email", "dummy@giblish.com")
|
51
|
+
@logger&.info { "Cloning done" }
|
52
|
+
p
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate_docs(ref)
|
56
|
+
srcdir = @repo_root.join(@doc_src_rel)
|
57
|
+
args = @giblish_args + %W[-g #{@ref_regexp} #{srcdir} #{@dstdir}]
|
58
|
+
|
59
|
+
@logger&.info { "Generate docs using parameters: #{args}" }
|
60
|
+
# run giblish with all args
|
61
|
+
EntryPoint.run(args, @logger)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|