giblish 0.7.6 → 1.0.0.rc2

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 +61 -0
  6. data/README.adoc +267 -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 +51 -32
  25. data/lib/giblish/adocsrc_providers.rb +23 -0
  26. data/lib/giblish/application.rb +213 -37
  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} +33 -9
  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 +472 -76
  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 +167 -321
  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 +190 -81
  84. data/.rubocop.yml +0 -8
  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 -218
  105. data/lib/giblish/buildindex.rb +0 -464
  106. data/lib/giblish/core.rb +0 -442
  107. data/lib/giblish/docconverter.rb +0 -310
  108. data/lib/giblish/docid.rb +0 -188
  109. data/lib/giblish/docinfo.rb +0 -100
  110. data/lib/giblish/indexheadings.rb +0 -255
  111. data/lib/giblish-search.cgi +0 -459
  112. data/scripts/hooks/post-receive +0 -57
  113. data/scripts/publish_html.sh +0 -99
data/lib/giblish/utils.rb CHANGED
@@ -1,23 +1,27 @@
1
1
  require "logger"
2
2
  require "pathname"
3
3
  require "fileutils"
4
+ require "find"
4
5
 
5
- class GiblogFormatter
6
- def call severity, datetime, progname, msg
7
- "#{datetime.strftime('%H:%M:%S')} #{severity} - #{msg}\n"
6
+ # The logger used from within giblish
7
+ class Giblog
8
+ # Defines the format for log messages from giblish.
9
+ class GiblogFormatter
10
+ def call(severity, datetime, _progname, msg)
11
+ "#{datetime.strftime("%H:%M:%S")} #{severity} - #{msg}\n"
12
+ end
8
13
  end
9
- end
10
14
 
11
- class Giblog
12
- def self.setup
15
+ # bootstrap the application-wide logger object
16
+ def self.setup(logger = nil)
17
+ @logger = logger unless logger.nil?
13
18
  return if defined? @logger
14
- @logger = Logger.new(STDOUT)
19
+
20
+ @logger = Logger.new($stdout)
15
21
  @logger.formatter = GiblogFormatter.new
16
- # @logger.formatter = proc do |severity, datetime, _progname, msg|
17
- # "#{datetime.strftime('%H:%M:%S')} #{severity} - #{msg}\n"
18
- # end
19
22
  end
20
23
 
24
+ # returns the application-wide logger instance.
21
25
  def self.logger
22
26
  unless defined? @logger
23
27
  puts "!!!! Error: Trying to access logger before setup !!!!"
@@ -28,282 +32,90 @@ class Giblog
28
32
  end
29
33
  end
30
34
 
31
-
32
-
33
35
  # Public: Contains a number of generic utility methods.
34
36
  module Giblish
35
-
36
- class UserInfoFormatter
37
- SEVERITY_LABELS = { 'WARN' => 'WARNING', 'FATAL' => 'FAILED' }
38
-
39
- # {:text=>"...",
40
- # :source_location=>#<Asciidoctor::Reader::Cursor:0x000055e65a8729e0
41
- # @file="<full adoc filename>",
42
- # @dir="<full src dir>",
43
- # @path="<only file name>",
44
- # @lineno=<src line no>
45
- # }
46
- def call severity, datetime, progname, msg
47
- message = case msg
48
- when ::String
49
- msg
50
- when ::Hash
51
- # asciidoctor seem to emit a hash with error text and source location info
52
- # for warnings and errors
53
- str = ""
54
- str << "Line #{msg[:source_location].lineno.to_s} - " if msg[:source_location]
55
- str << "#{msg[:text].to_s}" if msg[:text]
56
- str
57
- else
58
- msg.inspect
59
- end
60
- %(#{datetime.strftime('%H:%M:%S')} #{progname}: #{SEVERITY_LABELS[severity] || severity}: #{message}\n)
61
- end
62
- end
37
+ # This logger is customized to receive log messages via the Asciidoctor API.
38
+ # It parses the messages and 'source_location' objects from the Asciidoctor API
39
+ # into messages using an opinionated format.
40
+ #
41
+ # The output is written to both $stdout and an in-memory StringIO instance. The log level
42
+ # can be set separately for each of these output channels.
63
43
  class AsciidoctorLogger < ::Logger
44
+ attr_reader :max_severity, :in_mem_storage
64
45
 
65
- attr_reader :max_severity
66
- attr_reader :user_info_str
67
-
68
- def initialize user_info_log_level
69
- super(STDOUT,progname: "(from asciidoctor)", formatter: UserInfoFormatter.new)
70
- @user_info_str = StringIO.new
71
- @user_info_logger = ::Logger.new(@user_info_str, formatter: UserInfoFormatter.new, level: user_info_log_level)
72
- end
73
-
74
- def add severity, message = nil, progname = nil
75
- if (severity ||= UNKNOWN) > (@max_severity ||= severity)
76
- @max_severity = severity
77
- end
78
- @user_info_logger.add(severity,message,progname)
79
- super
80
- end
81
- end
82
-
83
- class DeploymentPaths
84
-
85
- attr_reader :web_path
86
-
87
- def initialize(web_path, search_asset_path)
88
- @search_assets_path = if search_asset_path.nil?
89
- nil
90
- else
91
- Pathname.new("/#{search_asset_path.to_s}").cleanpath
92
- end
93
- @web_path = if web_path.nil?
94
- nil
95
- else
96
- Pathname.new("/#{web_path.to_s}/web_assets").cleanpath
97
- end
98
- end
46
+ # log formatter specialized for formatting messages from
47
+ # asciidoctor's stdout, handles the different log record types that Asciidoctor
48
+ # emits
49
+ class UserInfoFormatter
50
+ SEVERITY_LABELS = {"WARN" => "WARNING", "FATAL" => "FAILED"}.freeze
99
51
 
100
- def search_assets_path(branch_dir=nil)
101
- if branch_dir.nil?
102
- @search_assets_path
103
- else
104
- @search_assets_path.join(branch_dir)
52
+ def call(severity, datetime, progname, msg)
53
+ %(#{datetime.strftime("%H:%M:%S")} #{progname}: #{SEVERITY_LABELS[severity] || severity}: #{UserInfoFormatter.adoc_to_message(msg)}\n)
105
54
  end
106
- end
107
-
108
- def search_assets_path=(path)
109
- @search_assets_path = path
110
- end
111
- end
112
-
113
- # Helper class to ease construction of different paths for input and output
114
- # files and directories
115
- class PathManager
116
- attr_reader :src_root_abs
117
- attr_reader :dst_root_abs
118
- attr_reader :resource_dir_abs
119
- attr_reader :search_assets_abs
120
-
121
- # Public:
122
- #
123
- # src_root - a string or pathname with the top directory of the input file
124
- # tree
125
- # dst_root - a string or pathname with the top directory of the output file
126
- # tree
127
- # resource_dir - a string or pathname with the directory containing
128
- # resources
129
- # create_search_asset_dir - true if this instance shall create a dir for storing
130
- # search artefacts, false otherwise
131
- def initialize(src_root, dst_root, resource_dir = nil,create_search_asset_dir=false)
132
- # Make sure that the source root exists in the file system
133
- @src_root_abs = Pathname.new(src_root).realpath
134
- self.dst_root_abs = dst_root
135
-
136
- self.search_assets_abs = create_search_asset_dir ?
137
- @dst_root_abs.join("search_assets") : nil
138
-
139
- # Make sure that the resource dir exists if user gives a path to it
140
- resource_dir && (@resource_dir_abs = Pathname.new(resource_dir).realpath)
141
- end
142
55
 
143
- def search_assets_abs=(path)
144
- if path.nil?
145
- @search_assets_abs = nil
146
- return
56
+ # The hash that can be emitted as the msg from asciidoctor have the
57
+ # following format:
58
+ # {:text=>"...",
59
+ # :source_location=>#<Asciidoctor::Reader::Cursor:0x000055e65a8729e0
60
+ # @file="<full adoc filename>",
61
+ # @dir="<full src dir>",
62
+ # @path="<only file name>",
63
+ # @lineno=<src line no>
64
+ # }
65
+ def self.adoc_to_message(msg)
66
+ case msg
67
+ when ::String
68
+ msg
69
+ when ::Hash
70
+ # asciidoctor seem to emit a hash with the following structure on errors:
71
+ # :text => String
72
+ # :source_location => Reader::Cursor with the following props:
73
+ # dir, file, lineno, path
74
+ # Only the lineno prop can be trusted when Asciidoctor is used via Giblish
75
+ #
76
+ src_loc = msg.fetch(:source_location, nil)
77
+ err_txt = msg.fetch(:text, "")
78
+ str = ""
79
+ str << "Line #{src_loc.lineno} - " if src_loc.lineno
80
+ str << err_txt
81
+ str
82
+ else
83
+ msg.inspect
84
+ end
147
85
  end
148
- # Make sure that the destination root exists and expand it to an
149
- # absolute path
150
- dir = Pathname.new(path)
151
- dir.mkpath
152
- @search_assets_abs = dir.realpath
153
86
  end
154
87
 
155
- def dst_root_abs=(dst_root)
156
- # Make sure that the destination root exists and expand it to an
157
- # absolute path
158
- Pathname.new(dst_root).mkpath
159
- @dst_root_abs = Pathname.new(dst_root).realpath
160
- end
161
-
162
- # Public: Get the relative path from the source root dir to the
163
- # directory where the supplied path points.
164
- #
165
- # in_path - an absolute or relative path to a file or dir
166
- def reldir_from_src_root(in_path)
167
- p = self.class.closest_dir in_path
168
- p.relative_path_from(@src_root_abs)
169
- end
170
-
171
- # Public: Get the relative path from the
172
- # directory where the supplied path points to
173
- # the src root dir
88
+ # The level is one of the standard ::Logger levels
174
89
  #
175
- # path - an absolute or relative path to a file or dir
176
- def reldir_to_src_root(path)
177
- src = self.class.closest_dir path
178
- @src_root_abs.relative_path_from(src)
90
+ # stdout_level:: the log level to use for gating the messages to stdout
91
+ # string_level:: the log level to use for gating the messages to the in-memory string.
92
+ # defaults to 'stdout_level' if not set.
93
+ def initialize(user_logger, string_level = nil)
94
+ # def initialize(stdout_level, string_level = nil)
95
+ string_level = stdout_level if string_level.nil?
96
+ @user_logger = user_logger
97
+
98
+ @max_severity = UNKNOWN
99
+
100
+ # create a new, internal logger that echos messages to an in-memory string
101
+ @in_mem_storage = StringIO.new
102
+ # @in_mem_logger = ::Logger.new(@in_mem_storage, formatter: UserInfoFormatter.new, level: string_level)
103
+ super(@in_mem_storage, progname: "(asciidoctor)", formatter: UserInfoFormatter.new, level: string_level)
179
104
  end
180
105
 
181
- # Public: Get the relative path from the dst root dir to the
182
- # directory where the supplied path points.
183
- #
184
- # path - an absolute or relative path to a file or dir
185
- def reldir_from_dst_root(path)
186
- dst = self.class.closest_dir path
187
- dst.relative_path_from(@dst_root_abs)
188
- end
106
+ def add(severity, message = nil, progname = nil, &block)
107
+ # add message to adoc log
108
+ super(severity, message.dup, progname)
189
109
 
190
- # Public: Get the relative path from the
191
- # directory where the supplied path points to
192
- # the dst root dir
193
- #
194
- # path - an absolute or relative path to a file or dir
195
- def reldir_to_dst_root(path)
196
- dst = self.class.closest_dir path
197
- @dst_root_abs.relative_path_from(dst)
198
- end
110
+ # add message to user log (wierd interface... see Logger::add(...))
111
+ message = yield if block
112
+ message ||= progname
113
+ @user_logger&.add(severity, "(asciidoctor) #{UserInfoFormatter.adoc_to_message(message)}")
199
114
 
200
- # return the destination dir corresponding to the given src path
201
- # the src path must exist in the file system
202
- def dst_abs_from_src_abs(src_path)
203
- src_abs = (self.class.to_pathname src_path).realpath
204
- src_rel = reldir_from_src_root src_abs
205
- @dst_root_abs.join(src_rel)
115
+ # update the maximum severity received by this logger
116
+ @max_severity = severity if severity != UNKNOWN && severity > @max_severity
206
117
  end
207
-
208
- # return the relative path from a generated document to
209
- # the supplied folder given the corresponding absolute source
210
- # file path
211
- def relpath_to_dir_after_generate(src_filepath,dir_path)
212
- dst_abs = dst_abs_from_src_abs(src_filepath)
213
- dir = self.class.to_pathname(dir_path)
214
- dir.relative_path_from(dst_abs)
215
- end
216
-
217
- def adoc_output_file(infile_path, extension)
218
- # Get absolute source dir path
219
- src_dir_abs = self.class.closest_dir infile_path
220
-
221
- # Get relative path from source root dir
222
- src_dir_rel = src_dir_abs.relative_path_from(@src_root_abs)
223
-
224
- # Get the destination path relative the absolute source
225
- # root
226
- dst_dir_abs = @dst_root_abs.realpath.join(src_dir_rel)
227
-
228
- # return full file path with correct extension
229
- dst_dir_abs + get_new_basename(infile_path, extension)
230
- end
231
-
232
- # Public: Get the path to the directory where to generate the given
233
- # file. The path is given as the relative path from the source adoc
234
- # file to the desired output directory (required by the Asciidoctor
235
- # API).
236
- #
237
- # infile_path - a string or Pathname containing the absolute path of the
238
- # source adoc file
239
- #
240
- # Returns: a Pathname with the relative path from the source file to the
241
- # output directory
242
- def adoc_output_dir(infile_path)
243
- # Get absolute source dir path
244
- src_abs = self.class.closest_dir infile_path
245
-
246
- # Get relative path from source root dir
247
- src_rel = src_abs.relative_path_from(@src_root_abs)
248
-
249
- # Get the destination path relative the absolute source
250
- # root
251
- dst_abs = @dst_root_abs.realpath.join(src_rel)
252
- dst_abs.relative_path_from(src_abs)
253
- end
254
-
255
- # return a pathname, regardless if the given path is a Pathname or
256
- # a string
257
- def self.to_pathname(path)
258
- path.is_a?(Pathname) ? path : Pathname.new(path)
259
- end
260
-
261
- # Public: Get the basename for a file by replacing the file
262
- # extention of the source file with the supplied one.
263
- #
264
- # src_filepath - the full path of the source file
265
- # file_ext - the file extention of the resulting file name
266
- #
267
- # Example
268
- #
269
- # Giblish::PathManager.get_new_basename(
270
- # "/my/old/file.txt","pdf") => "file.pdf"
271
- #
272
- # Returns: the basename of a file that uses the supplied file extention.
273
- def self.get_new_basename(src_filepath, file_ext)
274
- p = Pathname.new src_filepath
275
- newname = p.basename.to_s.reverse.sub(p.extname.reverse, ".").reverse
276
- newname << file_ext
277
- end
278
-
279
- # Public: Return the absolute path to the closest directory (defined as
280
- # - the parent dir when called with an existing file
281
- # - the directory itself when called with an existing directory
282
- # - the parent dir when called with a non-existing file/directory
283
- def self.closest_dir(in_path)
284
- sr = self.to_pathname(in_path)
285
- if sr.exist?
286
- sr.directory? ? sr.realpath : sr.dirname.realpath
287
- else
288
- sr.parent.expand_path
289
- end
290
- end
291
-
292
- # Public: Find the root directory of the git repo in which the
293
- # given dirpath resides.
294
- #
295
- # dirpath - a relative or absolute path to a directory that resides
296
- # within a git repo.
297
- #
298
- # Returns: the root direcotry of the git repo or nil if the input path
299
- # does not reside within a git repo.
300
- def self.find_gitrepo_root(dirpath)
301
- Pathname.new(dirpath).realpath.ascend do |p|
302
- git_dir = p.join(".git")
303
- return p if git_dir.directory?
304
- end
305
- end
306
- end # end of PathManager
118
+ end
307
119
 
308
120
  # Helper method that provides the user with a way of processing only the
309
121
  # lines within the asciidoc header block.
@@ -312,13 +124,15 @@ module Giblish
312
124
  # ex:
313
125
  # process_header_lines(file_path) do |line|
314
126
  # if line == "Quack!"
315
- # puts "Donald!"
127
+ # myvar = "Donald!"
316
128
  # 1
317
129
  # else
318
130
  # nil
319
131
  # end
320
132
  # end
321
- def process_header_lines(lines)
133
+ def process_header_lines(lines, &block)
134
+ return unless block
135
+
322
136
  state = "before_header"
323
137
  lines.each do |line|
324
138
  case state
@@ -337,15 +151,17 @@ module Giblish
337
151
  # ex:
338
152
  # process_header_lines_from_file(file_path) do |line|
339
153
  # if line == "Quack!"
340
- # puts "Donald!"
154
+ # myvar = "Donald!"
341
155
  # 1
342
156
  # else
343
157
  # nil
344
158
  # end
345
159
  # end
346
- def process_header_lines_from_file(path)
160
+ def process_header_lines_from_file(path, &block)
161
+ return unless block
162
+
347
163
  lines = File.readlines(path)
348
- process_header_lines(lines,&Proc.new)
164
+ process_header_lines(lines, &block)
349
165
  end
350
166
  module_function :process_header_lines_from_file
351
167
 
@@ -362,9 +178,15 @@ module Giblish
362
178
  module_function :with_captured_stderr
363
179
 
364
180
  # transforms strings to valid asciidoctor id strings
365
- def to_valid_id(input_str,id_prefix="_", id_separator="_")
366
- id_str = input_str.strip.downcase.gsub(%r{[^a-z0-9]+}, id_separator)
367
- id_str = "#{id_prefix}#{id_str}"
181
+ # in some cases you want to add a semi-unique checksum. If you do, you are no longer
182
+ # compatible with asciidoctor's generated ids
183
+ def to_valid_id(input_str, id_prefix = "_", id_separator = "_", use_checksum = false)
184
+ # use a basic checksum to reduce the risk for duplicate ids
185
+ check_sum = use_checksum ? input_str.chars.reduce(0) { |sum, c| sum + c.ord } : ""
186
+
187
+ id_str = input_str.strip.downcase.gsub(/[^a-z0-9]+/, id_separator)
188
+ id_str = "#{id_prefix}#{check_sum}#{id_prefix}#{id_str}"
189
+ id_str.gsub!(/#{Regexp.quote(id_separator)}+/, id_separator)
368
190
  id_str.chomp(id_separator)
369
191
  end
370
192
  module_function :to_valid_id
@@ -375,58 +197,82 @@ module Giblish
375
197
  # Ex
376
198
  # which('ruby') #=> /usr/bin/ruby
377
199
  def which(cmd)
378
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
379
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
380
- exts.each { |ext|
200
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
201
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
202
+ exts.each do |ext|
381
203
  exe = File.join(path, "#{cmd}#{ext}")
382
204
  return exe if File.executable?(exe) && !File.directory?(exe)
383
- }
205
+ end
384
206
  end
385
- return nil
207
+ nil
386
208
  end
387
209
  module_function :which
388
210
 
389
-
390
- # returns raw html that displays a search box to let the user
391
- # acces the text search functionality.
211
+ # Convert a string into a string where all characters forbidden as part of
212
+ # filenames are replaced by an underscore '_'.
392
213
  #
393
- # css - the name of the css file to use for the search result layout
394
- # cgi_path - the (uri) path to a cgi script that implements the server side
395
- # functionality of searching the text
396
- # opts:
397
- # :web_assets_top => string # the path to the 'web_assets' dir as seen when serving
398
- # the web server (eg www.mysite.com/blah/doc_root ->
399
- # web_assets_top shall be '/blah/doc_root')
400
- # :search_assets_top => string # the path to where the 'heading.json' file is located (
401
- # as seen from the local file system on the machine that
402
- # runs the search script)
403
- def generate_search_box_html(css, cgi_path, opts)
404
-
405
- # Replace the button with the below to use a text-only version of the btn
406
- # <button id="search" type="submit">Search</button>
407
- <<~SEARCH_INFO
408
- ++++
409
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
410
- <form class="example" action="#{cgi_path}" style="margin:20px 0px 20px 0px;max-width:790px">
411
- Search all documents:
412
- <input id="searchphrase" type="text" placeholder="Search.." name="searchphrase"/>
413
- <button id="search" type="submit"><i class="fa fa-search"></i></button>
414
- <br>
214
+ # returns:: a String most likely a valid filename in windows & linux
215
+ #
216
+ # A comprehensive list of forbidden chars in different file systems can be
217
+ # found here: https://stackoverflow.com/a/31976060
218
+ # In short, chars forbidden in any of Windows and Linux are:
219
+ # / < > : " \\ | ? *
220
+ def to_fs_str(str)
221
+ # printable chars -> '_'
222
+ tmp = str.gsub(/[\/<>:"\\|?*]/, "_")
223
+ # non-printable chars -> '_'
224
+ tmp.gsub!(/[\x00-\x1F]/, "_")
225
+ # remove heading/trailing spaces
226
+ tmp.strip!
227
+ # Windows disallows files ending in '.'
228
+ tmp += "_" if tmp.end_with?(".")
229
+
230
+ tmp
231
+ end
232
+ module_function :to_fs_str
415
233
 
416
- <input id="ignorecase" type="checkbox" value="true" name="ignorecase" checked/>
417
- <label for="ignorecase">Ignore Case</label>
418
- &nbsp;&nbsp;
419
- <input id="useregexp" type="checkbox" value="true" name="useregexp"/>
420
- <label for="useregexp">Use Regexp</label>
234
+ # Break a line into rows of max_length, using '-' semi-intelligently
235
+ # to split words if needed
236
+ #
237
+ # return:: an Array with the resulting rows
238
+ def break_line(line, max_length)
239
+ too_short = 3
240
+ return [line] if line.length <= too_short
241
+ raise ArgumentError, "max_length must be larger than #{too_short - 1}" if max_length < too_short
242
+
243
+ rows = []
244
+ row = ""
245
+
246
+ until line.empty?
247
+ word, _sep, _remaining = line.strip.partition(" ")
248
+ row_space = max_length - row.length
249
+
250
+ # start word with a space if row is not empty
251
+ sep = row.empty? ? "" : " "
252
+
253
+ # if word fits in row, just insert it and take next word
254
+ if row_space - (word.length + sep.length) >= 0
255
+ row = "#{row}#{sep}#{word}"
256
+ line = line.sub(word, "").strip
257
+ next
258
+ end
421
259
 
422
- <input type="hidden" name="searchassetstop" value="#{opts[:search_assets_top]}"</input>
423
- <input type="hidden" name="webassetstop" value="#{opts[:web_assets_top]}"</input>
424
- #{'<input type="hidden" name="css" value="' + css +'"</input>' unless css.nil? }
425
- </form>
426
- ++++
260
+ # shall we split word or just move it to next row?
261
+ if (row_space == max_length && word.length > row_space) ||
262
+ (word.length > too_short && (row_space > too_short) && (word.length - row_space).abs > too_short)
263
+ # we will split the word, using a '-'
264
+ first_part = word[0..row_space - (1 + sep.length)]
265
+ row = "#{row}#{sep}#{first_part}-"
266
+ line = line.sub(first_part, "").strip
267
+ end
427
268
 
428
- SEARCH_INFO
269
+ # start a new row
270
+ rows << row
271
+ row = ""
272
+ end
273
+ # need to add unfinished row if any
274
+ rows << row unless row.empty?
275
+ rows
429
276
  end
430
- module_function :generate_search_box_html
431
-
277
+ module_function :break_line
432
278
  end
@@ -1,3 +1,3 @@
1
1
  module Giblish
2
- VERSION = "0.7.6".freeze
2
+ VERSION = "1.0.0.rc2".freeze
3
3
  end
data/lib/giblish.rb CHANGED
@@ -1,18 +1,21 @@
1
1
  #!/usr/bin/env ruby
2
+ if /^2/.match?(RUBY_VERSION)
3
+ # suppress warnings for 'experimental' pattern matching
4
+ # for ruby versions < 3.x
5
+ require "warning"
6
+ Warning.ignore(/Pattern matching/)
7
+ end
2
8
 
3
9
  require_relative "giblish/version"
4
- require_relative "giblish/utils"
5
- require_relative "giblish/core"
6
- require_relative "giblish/buildindex"
7
- require_relative "giblish/cmdline"
8
- require_relative "giblish/pathtree"
9
10
  require_relative "giblish/application"
11
+ require_relative "giblish/search/request_manager"
12
+ require_relative "giblish/github_trigger/webhook_manager"
10
13
 
11
14
  module Giblish
15
+ # The main entry point to the giblish application
12
16
  class << self
13
-
14
17
  def application
15
- @application ||= Giblish::Application.new
18
+ @application ||= Giblish::EntryPoint
16
19
  end
17
20
  end
18
21
  end
@@ -0,0 +1,66 @@
1
+ #!/bin/bash
2
+ #
3
+ # this hook runs giblish on a document tree according to the
4
+ # users settings.
5
+ # Typically used to publish html documents when a push to a matching
6
+ # branch is detected.
7
+
8
+ # the git refs that should trigger a doc generation at update
9
+ TRIGGERING_REFS_REGEX=$'main'
10
+
11
+ # the relative path to a subfolder in the repo where the docs are
12
+ REPO_DOC_SUBFOLDER="docs"
13
+
14
+ # the staging repo root (where the working tree exists)
15
+ STAGING_REPO="/usr/local/git_staging/rillbert_se_staging"
16
+
17
+ # The web-server top dir for the published documents
18
+ DST_DIR="/var/www/rillbert_se/html/public/docs"
19
+
20
+ # path to a top dir of the styling css and other resources
21
+ RESOURCE_DIR="scripts/resources"
22
+
23
+ # the name of the css file to use for styling, without the 'css' extension
24
+ LAYOUT_STYLE="giblish"
25
+
26
+ # "true" runs an 'rm -rf' of the destination dir before generating new htmls
27
+ CLEAR_DST="true"
28
+
29
+ echo "post-receive hook running..."
30
+
31
+ # read the input that git sends to this hook
32
+ read oldrev newrev ref
33
+
34
+ # remove the 'refs/heads/' prefix from the git ref
35
+ ref="${ref/refs\/heads\//}"
36
+
37
+ # filter out refs that are irrelevant for doc generation
38
+ if [[ ! "${ref}" =~ "${TRIGGERING_REFS_REGEX}" ]]; then
39
+ echo "Ref '${ref}' received. Doing nothing: only refs matching the regex: /${TRIGGERING_REFS_REGEX}/ will trigger a doc generation."
40
+ exit 0
41
+ fi
42
+
43
+ echo "Document generation triggered by an update to ${ref}."
44
+ echo ""
45
+
46
+ # use a subshell with the correct working dir for the actual doc generation
47
+ (
48
+ cd "${STAGING_REPO}"
49
+
50
+ # need to unset the GIT_DIR env set by the invoking hook for giblish to work correctly
51
+ unset GIT_DIR
52
+
53
+ if [[ "${CLEAR_DST}" -eq "true" ]]; then
54
+ echo "Remove everything under ${DST_DIR}/"
55
+ rm -rf ${DST_DIR}/*
56
+ fi
57
+
58
+ # Generate html docs
59
+ echo "running giblish..."
60
+ giblish -a xrefstyle=basic \
61
+ --copy-asset-folders "_assets$" \
62
+ -g "${TRIGGERING_REFS_REGEX}" \
63
+ -r "${RESOURCE_DIR}" \
64
+ -s "${LAYOUT_STYLE}" \
65
+ "${REPO_DOC_SUBFOLDER}" "${DST_DIR}"
66
+ )
File without changes